mod_http_oauth2/mod_http_oauth2.lua
author Kim Alvefur <zash@zash.se>
Sat, 22 Apr 2023 14:06:41 +0200
changeset 5360 959dc350f2ad
parent 5359 41a418ebc60b
child 5361 eda3b078ba2c
permissions -rw-r--r--
mod_http_oauth2: Declare https as required of URIs in schema If util.jsonschema happens to gain support for 'pattern' (regular expression validation) then this would be picked up. Until then, declarative annotations are nice.
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";
5247
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
     9
local id = require "util.id";
4260
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;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    12
local random = require "util.random";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    13
local schema = require "util.jsonschema";
5213
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
    14
local set = require "util.set";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    15
local jwt = require"util.jwt";
5207
c60cff787d6a mod_http_oauth2: Return actually enabled response types in discovery
Kim Alvefur <zash@zash.se>
parents: 5206
diff changeset
    16
local it = require "util.iterators";
c60cff787d6a mod_http_oauth2: Return actually enabled response types in discovery
Kim Alvefur <zash@zash.se>
parents: 5206
diff changeset
    17
local array = require "util.array";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    18
local st = require "util.stanza";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    19
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    20
local function read_file(base_path, fn, required)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    21
	local f, err = io.open(base_path .. "/" .. fn);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    22
	if not f then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    23
		module:log(required and "error" or "debug", "Unable to load template file: %s", err);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    24
		if required then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    25
			return error("Failed to load templates");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    26
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    27
		return nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    28
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    29
	local data = assert(f:read("*a"));
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    30
	assert(f:close());
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    31
	return data;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    32
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    33
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    34
local template_path = module:get_option_path("oauth2_template_path", "html");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    35
local templates = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    36
	login = read_file(template_path, "login.html", true);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    37
	consent = read_file(template_path, "consent.html", true);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    38
	error = read_file(template_path, "error.html", true);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    39
	css = read_file(template_path, "style.css");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    40
	js = read_file(template_path, "script.js");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    41
};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    42
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    43
local site_name = module:get_option_string("site_name", module.host);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    44
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    45
local _render_html = require"util.interpolation".new("%b{}", st.xml_escape);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    46
local function render_page(template, data, sensitive)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    47
	data = data or {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    48
	data.site_name = site_name;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    49
	local resp = {
5222
1f4b768c831a mod_http_oauth2: Correct field name for HTTP response status code
Kim Alvefur <zash@zash.se>
parents: 5221
diff changeset
    50
		status_code = 200;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    51
		headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    52
			["Content-Type"] = "text/html; charset=utf-8";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    53
			["Content-Security-Policy"] = "default-src 'self'";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    54
			["X-Frame-Options"] = "DENY";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    55
			["Cache-Control"] = (sensitive and "no-store" or "no-cache")..", private";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    56
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    57
		body = _render_html(template, data);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    58
	};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    59
	return resp;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    60
end
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    61
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
    62
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
    63
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
    64
local default_access_ttl = module:get_option_number("oauth2_access_token_ttl", 86400);
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
    65
local default_refresh_ttl = module:get_option_number("oauth2_refresh_token_ttl", nil);
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
    66
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    67
-- Used to derive client_secret from client_id, set to enable stateless dynamic registration.
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    68
local registration_key = module:get_option_string("oauth2_registration_key");
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    69
local registration_algo = module:get_option_string("oauth2_registration_algorithm", "HS256");
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    70
local registration_options = module:get_option("oauth2_registration_options", { default_ttl = 60 * 60 * 24 * 90 });
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    71
5203
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5202
diff changeset
    72
local verification_key;
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    73
local jwt_sign, jwt_verify;
5200
6b63af56c8ac mod_http_oauth2: Remove error message
Kim Alvefur <zash@zash.se>
parents: 5199
diff changeset
    74
if registration_key then
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    75
	-- Tie it to the host if global
5203
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5202
diff changeset
    76
	verification_key = hashes.hmac_sha256(registration_key, module.host);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    77
	jwt_sign, jwt_verify = jwt.init(registration_algo, registration_key, registration_key, registration_options);
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
    78
end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
    79
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    80
local function parse_scopes(scope_string)
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    81
	return array(scope_string:gmatch("%S+"));
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    82
end
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    83
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
    84
local openid_claims = set.new({ "profile"; "email"; "address"; "phone" });
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
    85
5260
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
    86
local function filter_scopes(username, requested_scope_string)
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    87
	local selected_role, granted_scopes = nil, array();
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    88
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    89
	if requested_scope_string then -- Specific role(s) requested
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    90
		local requested_scopes = parse_scopes(requested_scope_string);
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    91
		for _, scope in ipairs(requested_scopes) do
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
    92
			if scope == "openid" or openid_claims:contains(scope) then
5259
001c8fdc91a4 mod_http_oauth2: Add support for the "openid" scope
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
    93
				granted_scopes:push(scope);
001c8fdc91a4 mod_http_oauth2: Add support for the "openid" scope
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
    94
			end
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    95
			if selected_role == nil and usermanager.user_can_assume_role(username, module.host, scope) then
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    96
				selected_role = scope;
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
    97
			end
5002
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
    98
		end
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
    99
	end
5ab134b7e510 mod_http_oauth2: Updates for Prosody's new role API (backwards-compatible)
Matthew Wild <mwild1@gmail.com>
parents: 4674
diff changeset
   100
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   101
	if not selected_role then
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   102
		-- By default use the users' default role
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   103
		selected_role = usermanager.get_user_role(username, module.host).name;
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   104
	end
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   105
	granted_scopes:push(selected_role);
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   106
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   107
	return granted_scopes:concat(" "), selected_role;
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   108
end
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   109
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   110
local function code_expires_in(code) --> number, seconds until code expires
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   111
	return os.difftime(code.expires, os.time());
4673
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
   112
end
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
   113
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   114
local function code_expired(code) --> boolean, true: has expired, false: still valid
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   115
	return code_expires_in(code) < 0;
4273
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   116
end
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   117
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   118
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
   119
	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
   120
end);
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   121
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   122
-- Periodically clear out unredeemed codes.  Does not need to be exact, expired
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   123
-- codes are rejected if tried. Mostly just to keep memory usage in check.
5358
39d59d857bfb mod_http_oauth2: Use new mod_cron API for periodic cleanup
Kim Alvefur <zash@zash.se>
parents: 5345
diff changeset
   124
module:hourly("Clear expired authorization codes", function()
4276
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   125
	local k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   126
	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
   127
		codes:set(k, nil);
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   128
		k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   129
	end
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   130
end)
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   131
5211
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   132
local function get_issuer()
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   133
	return (module:http_url(nil, "/"):gsub("/$", ""));
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   134
end
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   135
5213
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   136
local loopbacks = set.new({ "localhost", "127.0.0.1", "::1" });
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   137
local function is_secure_redirect(uri)
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   138
	local u = url.parse(uri);
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   139
	return u.scheme ~= "http" or loopbacks:contains(u.host);
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   140
end
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   141
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   142
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
   143
	return errors.new({
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   144
		type = "modify";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   145
		condition = "bad-request";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   146
		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
   147
		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
   148
		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
   149
	});
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   150
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   151
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   152
-- client_id / client_metadata are pretty large, filter out a subset of
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   153
-- properties that are deemed useful e.g. in case tokens issued to a certain
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   154
-- client needs to be revoked
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   155
local function client_subset(client)
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   156
	return { name = client.client_name; uri = client.client_uri };
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   157
end
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   158
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   159
local function new_access_token(token_jid, role, scope_string, client, id_token, refresh_token_info)
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   160
	local token_data = { oauth2_scopes = scope_string, oauth2_client = nil };
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   161
	if client then
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   162
		token_data.oauth2_client = client_subset(client);
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   163
	end
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   164
	if next(token_data) == nil then
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   165
		token_data = nil;
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   166
	end
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   167
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   168
	local refresh_token;
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   169
	local grant = refresh_token_info and refresh_token_info.grant;
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   170
	if not grant then
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   171
		-- No existing grant, create one
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   172
		grant = tokens.create_grant(token_jid, token_jid, default_refresh_ttl, token_data);
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   173
		-- Create refresh token for the grant if desired
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   174
		refresh_token = refresh_token_info ~= false and tokens.create_token(token_jid, grant, nil, nil, "oauth2-refresh");
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   175
	else
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   176
		-- Grant exists, reuse existing refresh token
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   177
		refresh_token = refresh_token_info.token;
5345
dcb93ffe64ae mod_http_oauth2: Fix error due to reference loop when using refresh token
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
   178
dcb93ffe64ae mod_http_oauth2: Fix error due to reference loop when using refresh token
Kim Alvefur <zash@zash.se>
parents: 5344
diff changeset
   179
		refresh_token_info.grant = nil; -- Prevent reference loop
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   180
	end
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   181
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   182
	local access_token, access_token_info = tokens.create_token(token_jid, grant, role, default_access_ttl, "oauth2");
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   183
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   184
	local expires_at = access_token_info.expires;
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   185
	return {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   186
		token_type = "bearer";
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   187
		access_token = access_token;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   188
		expires_in = expires_at and (expires_at - os.time()) or nil;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   189
		scope = scope_string;
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   190
		id_token = id_token;
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   191
		refresh_token = refresh_token or nil;
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   192
	};
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   193
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   194
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   195
local function get_redirect_uri(client, query_redirect_uri) -- record client, string : string
5223
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   196
	if not query_redirect_uri then
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   197
		if #client.redirect_uris ~= 1 then
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   198
			-- Client registered multiple URIs, it needs specify which one to use
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   199
			return;
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   200
		end
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   201
		-- When only a single URI is registered, that's the default
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   202
		return client.redirect_uris[1];
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   203
	end
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   204
	-- Verify the client-provided URI matches one previously registered
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   205
	for _, redirect_uri in ipairs(client.redirect_uris) do
5223
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   206
		if query_redirect_uri == redirect_uri then
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   207
			return redirect_uri
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   208
		end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   209
	end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   210
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   211
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   212
local grant_type_handlers = {};
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   213
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
   214
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   215
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
   216
	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
   217
	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
   218
	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
   219
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
   220
	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
   221
		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
   222
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   223
	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
   224
		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
   225
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   226
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   227
	local granted_jid = jid.join(request_username, request_host, request_resource);
5260
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   228
	local granted_scopes, granted_role = filter_scopes(request_username, params.scope);
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   229
	return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, nil));
3907
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
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   232
function response_type_handlers.code(client, params, granted_jid, id_token)
5195
f5a58cbe86e4 mod_http_oauth2: Derive scope from correct user details
Kim Alvefur <zash@zash.se>
parents: 5194
diff changeset
   233
	local request_username, request_host = jid.split(granted_jid);
5260
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   234
	if not request_host or request_host ~= module.host then
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   235
		return oauth_error("invalid_request", "invalid JID");
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   236
	end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   237
	local granted_scopes, granted_role = filter_scopes(request_username, params.scope);
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   238
5247
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
   239
	local code = id.medium();
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   240
	local ok = codes:set(params.client_id .. "#" .. code, {
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   241
		expires = os.time() + 600;
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   242
		granted_jid = granted_jid;
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   243
		granted_scopes = granted_scopes;
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   244
		granted_role = granted_role;
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   245
		id_token = id_token;
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   246
	});
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   247
	if not ok then
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   248
		return {status_code = 429};
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   249
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   250
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   251
	local redirect_uri = get_redirect_uri(client, params.redirect_uri);
5192
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   252
	if redirect_uri == "urn:ietf:wg:oauth:2.0:oob" then
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   253
		-- TODO some nicer template page
5210
31c62df82aa8 mod_http_oauth2: Clarify comment referencing mod_http_errors (thanks MattJ)
Kim Alvefur <zash@zash.se>
parents: 5209
diff changeset
   254
		-- mod_http_errors will set content-type to text/html if it catches this
31c62df82aa8 mod_http_oauth2: Clarify comment referencing mod_http_errors (thanks MattJ)
Kim Alvefur <zash@zash.se>
parents: 5209
diff changeset
   255
		-- event, if not text/plain is kept for the fallback text.
5192
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   256
		local response = { status_code = 200; headers = { content_type = "text/plain" } }
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   257
		response.body = module:context("*"):fire_event("http-message", {
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   258
			response = response;
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   259
			title = "Your authorization code";
5199
b4932915e773 mod_http_oauth2: Mention name of client when giving out OOB authorization code
Kim Alvefur <zash@zash.se>
parents: 5198
diff changeset
   260
			message = "Here's your authorization code, copy and paste it into " .. (client.client_name or "your client");
5192
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   261
			extra = code;
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   262
		}) or ("Here's your authorization code:\n%s\n"):format(code);
5194
1733f184e2bb mod_http_oauth2: Fix to actually return OOB response
Kim Alvefur <zash@zash.se>
parents: 5193
diff changeset
   263
		return response;
5223
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   264
	elseif not redirect_uri then
5251
dc27b997e969 mod_http_oauth2: Invoke mod_http_errors to render error on invalid redirect
Kim Alvefur <zash@zash.se>
parents: 5250
diff changeset
   265
		return 400;
5192
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   266
	end
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   267
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   268
	local redirect = url.parse(redirect_uri);
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   269
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   270
	local query = http.formdecode(redirect.query or "");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   271
	if type(query) ~= "table" then query = {}; end
5196
03aa9baa9ac3 mod_http_oauth2: Add support for 'iss' authz response parameter (RFC 9207)
Matthew Wild <mwild1@gmail.com>
parents: 5195
diff changeset
   272
	table.insert(query, { name = "code", value = code });
5211
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   273
	table.insert(query, { name = "iss", value = get_issuer() });
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   274
	if params.state then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   275
		table.insert(query, { name = "state", value = params.state });
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   276
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   277
	redirect.query = http.formencode(query);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   278
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   279
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   280
		status_code = 303;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   281
		headers = {
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   282
			location = url.build(redirect);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   283
		};
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   284
	}
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   285
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   286
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   287
-- Implicit flow
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   288
function response_type_handlers.token(client, params, granted_jid)
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   289
	local request_username, request_host = jid.split(granted_jid);
5260
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   290
	if not request_host or request_host ~= module.host then
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   291
		return oauth_error("invalid_request", "invalid JID");
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   292
	end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   293
	local granted_scopes, granted_role = filter_scopes(request_username, params.scope);
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   294
	local token_info = new_access_token(granted_jid, granted_role, granted_scopes, client, nil);
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   295
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   296
	local redirect = url.parse(get_redirect_uri(client, params.redirect_uri));
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   297
	token_info.state = params.state;
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   298
	redirect.fragment = http.formencode(token_info);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   299
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   300
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   301
		status_code = 303;
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   302
		headers = {
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   303
			location = url.build(redirect);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   304
		};
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   305
	}
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   306
end
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   307
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   308
local function make_client_secret(client_id) --> client_secret
5203
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5202
diff changeset
   309
	return hashes.hmac_sha256(verification_key, client_id, true);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   310
end
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   311
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   312
local function verify_client_secret(client_id, client_secret)
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   313
	return hashes.equals(make_client_secret(client_id), client_secret);
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   314
end
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   315
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   316
function grant_type_handlers.authorization_code(params)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   317
	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
   318
	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
   319
	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
   320
	if params.scope and params.scope ~= "" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   321
		return oauth_error("invalid_scope", "unknown scope requested");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   322
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   323
5256
85f0c6c1c24f mod_http_oauth2: Fix attempt to index a boolean value
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
   324
	local client_ok, client = jwt_verify(params.client_id);
85f0c6c1c24f mod_http_oauth2: Fix attempt to index a boolean value
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
   325
	if not client_ok then
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   326
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   327
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   328
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   329
	if not verify_client_secret(params.client_id, 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
   330
		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
   331
		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
   332
	end
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   333
	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
   334
	if err then error(err); end
5218
d5492bc861f6 mod_http_oauth2: Remove authorization codes after use
Kim Alvefur <zash@zash.se>
parents: 5217
diff changeset
   335
	-- MUST NOT use the authorization code more than once, so remove it to
d5492bc861f6 mod_http_oauth2: Remove authorization codes after use
Kim Alvefur <zash@zash.se>
parents: 5217
diff changeset
   336
	-- prevent a second attempted use
d5492bc861f6 mod_http_oauth2: Remove authorization codes after use
Kim Alvefur <zash@zash.se>
parents: 5217
diff changeset
   337
	codes:set(params.client_id .. "#" .. params.code, nil);
4273
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   338
	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
   339
		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
   340
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   341
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   342
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   343
	return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token));
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   344
end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   345
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   346
function grant_type_handlers.refresh_token(params)
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   347
	if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   348
	if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   349
	if not params.refresh_token then return oauth_error("invalid_request", "missing 'refresh_token'"); end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   350
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   351
	local client_ok, client = jwt_verify(params.client_id);
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   352
	if not client_ok then
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   353
		return oauth_error("invalid_client", "incorrect credentials");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   354
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   355
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   356
	if not verify_client_secret(params.client_id, params.client_secret) then
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   357
		module:log("debug", "client_secret mismatch");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   358
		return oauth_error("invalid_client", "incorrect credentials");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   359
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   360
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   361
	local refresh_token_info = tokens.get_token_info(params.refresh_token);
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   362
	if not refresh_token_info or refresh_token_info.purpose ~= "oauth2-refresh" then
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   363
		return oauth_error("invalid_grant", "invalid refresh token");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   364
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   365
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   366
	-- new_access_token() requires the actual token
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   367
	refresh_token_info.token = params.refresh_token;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   368
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   369
	return json.encode(new_access_token(
5344
cd195283127f mod_http_oauth2: Fix table index error when using refresh token
Kim Alvefur <zash@zash.se>
parents: 5341
diff changeset
   370
		refresh_token_info.jid, refresh_token_info.role, refresh_token_info.grant.data.oauth2_scopes, client, nil, refresh_token_info
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   371
	));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   372
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   373
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   374
-- Used to issue/verify short-lived tokens for the authorization process below
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   375
local new_user_token, verify_user_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 });
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   376
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   377
-- From the given request, figure out if the user is authenticated and has granted consent yet
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   378
-- As this requires multiple steps (seek credentials, seek consent), we have a lot of state to
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   379
-- carry around across requests. We also need to protect against CSRF and session mix-up attacks
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   380
-- (e.g. the user may have multiple concurrent flows in progress, session cookies aren't unique
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   381
--  to one of them).
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   382
-- Our strategy here is to preserve the original query string (containing the authz request), and
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   383
-- encode the rest of the flow in form POSTs.
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   384
local function get_auth_state(request)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   385
	local form = request.method == "POST"
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   386
	         and request.body
5280
67777cb7353d mod_http_oauth2: Pedantic optimization
Kim Alvefur <zash@zash.se>
parents: 5277
diff changeset
   387
	         and request.body ~= ""
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   388
	         and request.headers.content_type == "application/x-www-form-urlencoded"
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   389
	         and http.formdecode(request.body);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   390
5281
a1055024b94e mod_http_oauth2: Stricten check of urlencoded form data
Kim Alvefur <zash@zash.se>
parents: 5280
diff changeset
   391
	if type(form) ~= "table" then return {}; end
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   392
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   393
	if not form.user_token then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   394
		-- First step: login
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   395
		local username = encodings.stringprep.nodeprep(form.username);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   396
		local password = encodings.stringprep.saslprep(form.password);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   397
		if not (username and password) or not usermanager.test_password(username, module.host, password) then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   398
			return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   399
				error = "Invalid username/password";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   400
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   401
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   402
		return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   403
			user = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   404
				username = username;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   405
				host = module.host;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   406
				token = new_user_token({ username = username, host = module.host });
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   407
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   408
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   409
	elseif form.user_token and form.consent then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   410
		-- Second step: consent
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   411
		local ok, user = verify_user_token(form.user_token);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   412
		if not ok then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   413
			return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   414
				error = user == "token-expired" and "Session expired - try again" or nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   415
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   416
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   417
5275
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   418
		local scope = array():append(form):filter(function(field)
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   419
			return field.name == "scope";
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   420
		end):pluck("value"):concat(" ");
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   421
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   422
		user.token = form.user_token;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   423
		return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   424
			user = user;
5275
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   425
			scope = scope;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   426
			consent = form.consent == "granted";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   427
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   428
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   429
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   430
	return {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   431
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   432
5226
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   433
local function get_request_credentials(request)
5228
cd5cf4cc6304 mod_http_oauth2: Fail early when no authorization header present
Matthew Wild <mwild1@gmail.com>
parents: 5227
diff changeset
   434
	if not request.headers.authorization then return; end
cd5cf4cc6304 mod_http_oauth2: Fail early when no authorization header present
Matthew Wild <mwild1@gmail.com>
parents: 5227
diff changeset
   435
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   436
	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
   437
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   438
	if auth_type == "Basic" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   439
		local creds = base64.decode(auth_data);
5226
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   440
		if not creds then return; end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   441
		local username, password = string.match(creds, "^([^:]+):(.*)$");
5226
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   442
		if not username then return; end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   443
		return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   444
			type = "basic";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   445
			username = username;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   446
			password = password;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   447
		};
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   448
	elseif auth_type == "Bearer" then
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   449
		return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   450
			type = "bearer";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   451
			bearer_token = auth_data;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   452
		};
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   453
	end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   454
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   455
	return nil;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   456
end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   457
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   458
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
   459
	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
   460
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   461
	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
   462
		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
   463
		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
   464
		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
   465
		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
   466
			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
   467
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   468
		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
   469
			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
   470
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   471
		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
   472
			local granted_jid = jid.join(request_username, request_host, request_resource);
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   473
			return json.encode(new_access_token(granted_jid, nil, nil, nil));
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   474
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   475
		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
   476
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   477
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   478
	-- TODO How would this make sense with components?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   479
	-- Have an admin authenticate maybe?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   480
	response_type_handlers.code = nil;
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   481
	response_type_handlers.token = nil;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   482
	grant_type_handlers.authorization_code = nil;
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   483
end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   484
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   485
-- OAuth errors should be returned to the client if possible, i.e. by
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   486
-- appending the error information to the redirect_uri and sending the
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   487
-- redirect to the user-agent. In some cases we can't do this, e.g. if
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   488
-- the redirect_uri is missing or invalid. In those cases, we render an
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   489
-- error directly to the user-agent.
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   490
local function error_response(request, err)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   491
	local q = request.url.query and http.formdecode(request.url.query);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   492
	local redirect_uri = q and q.redirect_uri;
5221
6a27effb3ef0 mod_http_oauth2: Fix incorrect function name (thanks Zash/luacheck)
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
   493
	if not redirect_uri or not is_secure_redirect(redirect_uri) then
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   494
		module:log("warn", "Missing or invalid redirect_uri <%s>, rendering error to user-agent", redirect_uri or "");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   495
		return render_page(templates.error, { error = err });
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   496
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   497
	local redirect_query = url.parse(redirect_uri);
5233
c24a622a7b85 mod_http_oauth2: Fix appending of query parts in error redirects
Kim Alvefur <zash@zash.se>
parents: 5232
diff changeset
   498
	local sep = redirect_query.query and "&" or "?";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   499
	redirect_uri = redirect_uri
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   500
		.. sep .. http.formencode(err.extra.oauth2_response)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   501
		.. "&" .. http.formencode({ state = q.state, iss = get_issuer() });
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   502
	module:log("warn", "Sending error response to client via redirect to %s", redirect_uri);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   503
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   504
		status_code = 303;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   505
		headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   506
			location = redirect_uri;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   507
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   508
	};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   509
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   510
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   511
local allowed_grant_type_handlers = module:get_option_set("allowed_oauth2_grant_types", {"authorization_code", "password", "refresh_token"})
5191
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   512
for handler_type in pairs(grant_type_handlers) do
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   513
	if not allowed_grant_type_handlers:contains(handler_type) then
5234
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   514
		module:log("debug", "Grant type %q disabled", handler_type);
5191
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   515
		grant_type_handlers[handler_type] = nil;
5234
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   516
	else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   517
		module:log("debug", "Grant type %q enabled", handler_type);
5191
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   518
	end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   519
end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   520
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   521
-- "token" aka implicit flow is considered insecure
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   522
local allowed_response_type_handlers = module:get_option_set("allowed_oauth2_response_types", {"code"})
5202
2e8a7a0f932d mod_http_oauth2: Fix response type config
Kim Alvefur <zash@zash.se>
parents: 5200
diff changeset
   523
for handler_type in pairs(response_type_handlers) do
2e8a7a0f932d mod_http_oauth2: Fix response type config
Kim Alvefur <zash@zash.se>
parents: 5200
diff changeset
   524
	if not allowed_response_type_handlers:contains(handler_type) then
5234
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   525
		module:log("debug", "Response type %q disabled", handler_type);
5235
bef543068077 mod_http_oauth2: Fix to disable disabled response handlers correctly
Kim Alvefur <zash@zash.se>
parents: 5234
diff changeset
   526
		response_type_handlers[handler_type] = nil;
5234
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   527
	else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   528
		module:log("debug", "Response type %q enabled", handler_type);
5191
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   529
	end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   530
end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   531
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   532
function handle_token_grant(event)
5227
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   533
	local credentials = get_request_credentials(event.request);
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   534
3938
469408682152 mod_http_oauth2: Set content type on successful repsponses (fixes #1501)
Kim Alvefur <zash@zash.se>
parents: 3924
diff changeset
   535
	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
   536
	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
   537
	if not params then
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   538
		return error_response(event.request, oauth_error("invalid_request"));
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   539
	end
5227
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   540
5229
3439eb37f23b mod_http_oauth2: token endpoint: handle missing credentials
Matthew Wild <mwild1@gmail.com>
parents: 5228
diff changeset
   541
	if credentials and credentials.type == "basic" then
5227
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   542
		params.client_id = http.urldecode(credentials.username);
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   543
		params.client_secret = http.urldecode(credentials.password);
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   544
	end
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   545
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   546
	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
   547
	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
   548
	if not grant_handler then
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   549
		return error_response(event.request, oauth_error("unsupported_grant_type"));
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   550
	end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   551
	return grant_handler(params);
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   552
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   553
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   554
local function handle_authorization_request(event)
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   555
	local request = event.request;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   556
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
diff changeset
   557
	if not request.url.query then
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   558
		return error_response(request, oauth_error("invalid_request"));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   559
	end
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
diff changeset
   560
	local params = http.formdecode(request.url.query);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   561
	if not params then
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   562
		return error_response(request, oauth_error("invalid_request"));
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   563
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   564
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   565
	if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   566
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   567
	local ok, client = jwt_verify(params.client_id);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   568
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   569
	if not ok then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   570
		return oauth_error("invalid_client", "incorrect credentials");
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   571
	end
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   572
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   573
	local auth_state = get_auth_state(request);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   574
	if not auth_state.user then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   575
		-- Render login page
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   576
		return render_page(templates.login, { state = auth_state, client = client });
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   577
	elseif auth_state.consent == nil then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   578
		-- Render consent page
5277
40be37652d70 mod_http_oauth2: Fix traceback on missing 'scope' parameter
Kim Alvefur <zash@zash.se>
parents: 5275
diff changeset
   579
		return render_page(templates.consent, { state = auth_state; client = client; scopes = parse_scopes(params.scope or "") }, true);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   580
	elseif not auth_state.consent then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   581
		-- Notify client of rejection
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   582
		return error_response(request, oauth_error("access_denied"));
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   583
	end
5275
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   584
	-- else auth_state.consent == true
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   585
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   586
	params.scope = auth_state.scope;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   587
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   588
	local user_jid = jid.join(auth_state.user.username, module.host);
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   589
	local client_secret = make_client_secret(params.client_id);
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   590
	local id_token_signer = jwt.new_signer("HS256", client_secret);
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   591
	local id_token = id_token_signer({
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   592
		iss = get_issuer();
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   593
		sub = url.build({ scheme = "xmpp"; path = user_jid });
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   594
		aud = params.client_id;
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   595
		nonce = params.nonce;
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   596
	});
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   597
	local response_type = params.response_type;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   598
	local response_handler = response_type_handlers[response_type];
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   599
	if not response_handler then
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   600
		return error_response(request, oauth_error("unsupported_response_type"));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   601
	end
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   602
	return response_handler(client, params, user_jid, id_token);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   603
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   604
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   605
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
   606
	local request, response = event.request, event.response;
5269
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   607
	if request.headers.authorization then
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   608
		local credentials = get_request_credentials(request);
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   609
		if not credentials or credentials.type ~= "basic" then
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   610
			response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   611
			return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   612
		end
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   613
		-- OAuth "client" credentials
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   614
		if not verify_client_secret(credentials.username, credentials.password) then
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   615
			return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   616
		end
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   617
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   618
5271
60e0bc35de33 mod_http_oauth2: Relax payload content type checking in revocation
Kim Alvefur <zash@zash.se>
parents: 5270
diff changeset
   619
	local form_data = http.formdecode(event.request.body or "");
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   620
	if not form_data or not form_data.token then
5271
60e0bc35de33 mod_http_oauth2: Relax payload content type checking in revocation
Kim Alvefur <zash@zash.se>
parents: 5270
diff changeset
   621
		response.headers.accept = "application/x-www-form-urlencoded";
60e0bc35de33 mod_http_oauth2: Relax payload content type checking in revocation
Kim Alvefur <zash@zash.se>
parents: 5270
diff changeset
   622
		return 415;
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   623
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   624
	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
   625
	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
   626
		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
   627
		return 500;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   628
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   629
	return 200;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   630
end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   631
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   632
local registration_schema = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   633
	type = "object";
5241
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   634
	required = {
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   635
		-- These are shown to users in the template
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   636
		"client_name";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   637
		"client_uri";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   638
		-- We need at least one redirect URI for things to work
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   639
		"redirect_uris";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   640
	};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   641
	properties = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   642
		redirect_uris = { type = "array"; minLength = 1; items = { type = "string"; format = "uri" } };
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   643
		token_endpoint_auth_method = { type = "string"; enum = { "none"; "client_secret_post"; "client_secret_basic" } };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   644
		grant_types = {
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   645
			type = "array";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   646
			items = {
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   647
				type = "string";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   648
				enum = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   649
					"authorization_code";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   650
					"implicit";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   651
					"password";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   652
					"client_credentials";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   653
					"refresh_token";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   654
					"urn:ietf:params:oauth:grant-type:jwt-bearer";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   655
					"urn:ietf:params:oauth:grant-type:saml2-bearer";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   656
				};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   657
			};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   658
		};
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   659
		response_types = { type = "array"; items = { type = "string"; enum = { "code"; "token" } } };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   660
		client_name = { type = "string" };
5360
959dc350f2ad mod_http_oauth2: Declare https as required of URIs in schema
Kim Alvefur <zash@zash.se>
parents: 5359
diff changeset
   661
		client_uri = { type = "string"; format = "uri"; pattern = "^https:" };
959dc350f2ad mod_http_oauth2: Declare https as required of URIs in schema
Kim Alvefur <zash@zash.se>
parents: 5359
diff changeset
   662
		logo_uri = { type = "string"; format = "uri"; pattern = "^https:"  };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   663
		scope = { type = "string" };
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   664
		contacts = { type = "array"; items = { type = "string" } };
5360
959dc350f2ad mod_http_oauth2: Declare https as required of URIs in schema
Kim Alvefur <zash@zash.se>
parents: 5359
diff changeset
   665
		tos_uri = { type = "string"; format = "uri"; pattern = "^https:"  };
959dc350f2ad mod_http_oauth2: Declare https as required of URIs in schema
Kim Alvefur <zash@zash.se>
parents: 5359
diff changeset
   666
		policy_uri = { type = "string"; format = "uri"; pattern = "^https:"  };
959dc350f2ad mod_http_oauth2: Declare https as required of URIs in schema
Kim Alvefur <zash@zash.se>
parents: 5359
diff changeset
   667
		jwks_uri = { type = "string"; format = "uri"; pattern = "^https:"  };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   668
		jwks = { type = "object"; description = "JSON Web Key Set, RFC 7517" };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   669
		software_id = { type = "string"; format = "uuid" };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   670
		software_version = { type = "string" };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   671
	};
5282
d94dba396f9f mod_http_oauth2: Declare additional client registration fields as strings
Kim Alvefur <zash@zash.se>
parents: 5281
diff changeset
   672
	-- Localized versions of descriptive properties and URIs
d94dba396f9f mod_http_oauth2: Declare additional client registration fields as strings
Kim Alvefur <zash@zash.se>
parents: 5281
diff changeset
   673
	additionalProperties = { type = "string" };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   674
}
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   675
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   676
function create_client(client_metadata)
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   677
	if not schema.validate(registration_schema, client_metadata) then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   678
		return nil, oauth_error("invalid_request", "Failed schema validation.");
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   679
	end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   680
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   681
	local client_uri = url.parse(client_metadata.client_uri);
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   682
	if not client_uri or client_uri.scheme ~= "https" then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   683
		return nil, oauth_error("invalid_request", "Missing, invalid or insecure client_uri");
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   684
	end
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   685
5243
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   686
	for _, redirect_uri in ipairs(client_metadata.redirect_uris) do
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   687
		local components = url.parse(redirect_uri);
5244
001908044d0d mod_http_oauth2: Validate that redirect URIs are absolute
Kim Alvefur <zash@zash.se>
parents: 5243
diff changeset
   688
		if not components or not components.scheme then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   689
			return nil, oauth_error("invalid_request", "Invalid redirect URI.");
5245
65892dd1d4ae mod_http_oauth2: Reject insecure redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5244
diff changeset
   690
		elseif components.scheme == "http" and components.host ~= "localhost" then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   691
			return nil, oauth_error("invalid_request", "Insecure redirect URI forbidden (except http://localhost)");
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   692
		elseif components.scheme == "https" and components.host ~= client_uri.host then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   693
			return nil, oauth_error("invalid_request", "Redirects must use the same hostname as client_uri");
5246
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   694
		end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   695
	end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   696
5248
fa7bd721a3f6 mod_http_oauth2: Fix validation of informative URIs
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
   697
	for field, prop_schema in pairs(registration_schema.properties) do
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   698
		if field ~= "client_uri" and prop_schema.format == "uri" and client_metadata[field] then
5246
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   699
			local components = url.parse(client_metadata[field]);
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   700
			if components.scheme ~= "https" then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   701
				return nil, oauth_error("invalid_request", "Insecure URI forbidden");
5246
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   702
			end
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   703
			if components.authority ~= client_uri.authority then
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   704
				return nil, oauth_error("invalid_request", "Informative URIs must have the same hostname");
5246
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   705
			end
5243
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   706
		end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   707
	end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   708
5247
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
   709
	-- Ensure each signed client_id JWT is unique, short ID and issued at
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
   710
	-- timestamp should be sufficient to rule out brute force attacks
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
   711
	client_metadata.nonce = id.short();
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   712
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   713
	-- Do we want to keep everything?
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   714
	local client_id = jwt_sign(client_metadata);
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   715
	local client_secret = make_client_secret(client_id);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   716
5225
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
   717
	client_metadata.client_id = client_id;
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
   718
	client_metadata.client_secret = client_secret;
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
   719
	client_metadata.client_id_issued_at = os.time();
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
   720
	client_metadata.client_secret_expires_at = 0;
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
   721
5206
b81fd0d22c66 mod_http_oauth2: Calculate client secret expiry in registration response
Kim Alvefur <zash@zash.se>
parents: 5205
diff changeset
   722
	if not registration_options.accept_expired then
5225
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
   723
		client_metadata.client_secret_expires_at = client_metadata.client_id_issued_at + (registration_options.default_ttl or 3600);
5206
b81fd0d22c66 mod_http_oauth2: Calculate client secret expiry in registration response
Kim Alvefur <zash@zash.se>
parents: 5205
diff changeset
   724
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   725
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   726
	return client_metadata;
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   727
end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   728
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   729
local function handle_register_request(event)
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   730
	local request = event.request;
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   731
	local client_metadata, err = json.decode(request.body);
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   732
	if err then
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   733
		return oauth_error("invalid_request", "Invalid JSON");
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   734
	end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   735
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   736
	local response, err = create_client(client_metadata);
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   737
	if err then return err end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   738
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   739
	return {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   740
		status_code = 201;
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   741
		headers = { content_type = "application/json" };
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   742
		body = json.encode(response);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   743
	};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   744
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   745
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   746
if not registration_key then
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   747
	module:log("info", "No 'oauth2_registration_key', dynamic client registration disabled")
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   748
	handle_authorization_request = nil
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   749
	handle_register_request = nil
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   750
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   751
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   752
local function handle_userinfo_request(event)
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   753
	local request = event.request;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   754
	local credentials = get_request_credentials(request);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   755
	if not credentials or not credentials.bearer_token then
5340
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5339
diff changeset
   756
		module:log("debug", "Missing credentials for UserInfo endpoint: %q", credentials)
5339
53c6f49dcbb8 mod_http_oauth2: Correct error code when missing credentials for userinfo
Kim Alvefur <zash@zash.se>
parents: 5284
diff changeset
   757
		return 401;
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   758
	end
5340
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5339
diff changeset
   759
	local token_info,err = tokens.get_token_info(credentials.bearer_token);
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   760
	if not token_info then
5340
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5339
diff changeset
   761
		module:log("debug", "UserInfo query failed token validation: %s", err)
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   762
		return 403;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   763
	end
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   764
	local scopes = set.new()
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   765
	if type(token_info.grant.data) == "table" and type(token_info.grant.data.oauth2_scopes) == "string" then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   766
		scopes:add_list(parse_scopes(token_info.grant.data.oauth2_scopes));
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   767
	else
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   768
		module:log("debug", "token_info = %q", token_info)
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   769
	end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   770
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   771
	if not scopes:contains("openid") then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   772
		module:log("debug", "Missing the 'openid' scope in %q", scopes)
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   773
		-- The 'openid' scope is required for access to this endpoint.
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   774
		return 403;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   775
	end
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   776
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   777
	local user_info = {
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   778
		iss = get_issuer();
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   779
		sub = url.build({ scheme = "xmpp"; path = token_info.jid });
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   780
	}
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   781
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   782
	local token_claims = set.intersection(openid_claims, scopes);
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   783
	if not token_claims:empty() then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   784
		-- Another module can do that
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   785
		module:fire_event("token/userinfo", {
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   786
			token = token_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   787
			claims = token_claims;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   788
			username = jid.split(token_info.jid);
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   789
			userinfo = user_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   790
		});
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   791
	end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   792
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   793
	return {
5262
9629971e307f mod_http_oauth2: Fix userinfo status code off-by-one
Kim Alvefur <zash@zash.se>
parents: 5261
diff changeset
   794
		status_code = 200;
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   795
		headers = { content_type = "application/json" };
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   796
		body = json.encode(user_info);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   797
	};
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   798
end
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   799
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   800
module:depends("http");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   801
module:provides("http", {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   802
	route = {
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   803
		-- User-facing login and consent view
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   804
		["GET /authorize"] = handle_authorization_request;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   805
		["POST /authorize"] = handle_authorization_request;
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   806
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   807
		-- Create OAuth client
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   808
		["POST /register"] = handle_register_request;
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   809
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   810
		["POST /token"] = handle_token_grant;
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   811
		["POST /revoke"] = handle_revocation_request;
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   812
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
   813
		-- OpenID
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
   814
		["GET /userinfo"] = handle_userinfo_request;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   815
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   816
		-- Optional static content for templates
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   817
		["GET /style.css"] = templates.css and {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   818
			headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   819
				["Content-Type"] = "text/css";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   820
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   821
			body = _render_html(templates.css, module:get_option("oauth2_template_style"));
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   822
		} or nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   823
		["GET /script.js"] = templates.js and {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   824
			headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   825
				["Content-Type"] = "text/javascript";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   826
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   827
			body = templates.js;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   828
		} or nil;
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   829
	};
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   830
});
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   831
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   832
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
   833
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   834
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
   835
	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
   836
	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
   837
		return;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   838
	end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   839
	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
   840
	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
   841
	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
   842
end, 5);
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   843
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   844
-- OIDC Discovery
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   845
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   846
module:provides("http", {
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   847
	name = "oauth2-discovery";
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   848
	default_path = "/.well-known/oauth-authorization-server";
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   849
	route = {
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   850
		["GET"] = {
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   851
			headers = { content_type = "application/json" };
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   852
			body = json.encode {
5267
381c62ef52aa mod_http_oauth2: Group metadata section into OAuth and OpenID
Kim Alvefur <zash@zash.se>
parents: 5266
diff changeset
   853
				-- RFC 8414: OAuth 2.0 Authorization Server Metadata
5211
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   854
				issuer = get_issuer();
5204
afed7d5bd65c mod_http_oauth2: Advertise endpoints that are enabled
Kim Alvefur <zash@zash.se>
parents: 5203
diff changeset
   855
				authorization_endpoint = handle_authorization_request and module:http_url() .. "/authorize" or nil;
afed7d5bd65c mod_http_oauth2: Advertise endpoints that are enabled
Kim Alvefur <zash@zash.se>
parents: 5203
diff changeset
   856
				token_endpoint = handle_token_grant and module:http_url() .. "/token" or nil;
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   857
				jwks_uri = nil; -- TODO?
5204
afed7d5bd65c mod_http_oauth2: Advertise endpoints that are enabled
Kim Alvefur <zash@zash.se>
parents: 5203
diff changeset
   858
				registration_endpoint = handle_register_request and module:http_url() .. "/register" or nil;
5259
001c8fdc91a4 mod_http_oauth2: Add support for the "openid" scope
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
   859
				scopes_supported = usermanager.get_all_roles and array(it.keys(usermanager.get_all_roles(module.host))):push("openid")
001c8fdc91a4 mod_http_oauth2: Add support for the "openid" scope
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
   860
					or { "prosody:restricted"; "prosody:user"; "prosody:admin"; "prosody:operator"; "openid" };
5207
c60cff787d6a mod_http_oauth2: Return actually enabled response types in discovery
Kim Alvefur <zash@zash.se>
parents: 5206
diff changeset
   861
				response_types_supported = array(it.keys(response_type_handlers));
5196
03aa9baa9ac3 mod_http_oauth2: Add support for 'iss' authz response parameter (RFC 9207)
Matthew Wild <mwild1@gmail.com>
parents: 5195
diff changeset
   862
				authorization_response_iss_parameter_supported = true;
5267
381c62ef52aa mod_http_oauth2: Group metadata section into OAuth and OpenID
Kim Alvefur <zash@zash.se>
parents: 5266
diff changeset
   863
381c62ef52aa mod_http_oauth2: Group metadata section into OAuth and OpenID
Kim Alvefur <zash@zash.se>
parents: 5266
diff changeset
   864
				-- OpenID
381c62ef52aa mod_http_oauth2: Group metadata section into OAuth and OpenID
Kim Alvefur <zash@zash.se>
parents: 5266
diff changeset
   865
				userinfo_endpoint = handle_register_request and module:http_url() .. "/userinfo" or nil;
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   866
			};
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   867
		};
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   868
	};
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   869
});
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   870
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
   871
module:shared("tokenauth/oauthbearer_config").oidc_discovery_url = module:http_url("oauth2-discovery", "/.well-known/oauth-authorization-server");