mod_http_oauth2/mod_http_oauth2.lua
author Kim Alvefur <zash@zash.se>
Mon, 31 Jul 2023 02:07:58 +0200
changeset 5630 a44af1b646f5
parent 5623 81042c2a235a
child 5632 9aace51c3637
permissions -rw-r--r--
mod_http_oauth2: Optionally enforce authentication on revocation endpoint But why do OAuth require this? If a token leaks, why couldn't anyone revoke it?
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
5505
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     1
local usermanager = require "core.usermanager";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     2
local url = require "socket.url";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     3
local array = require "util.array";
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
     4
local cache = require "util.cache";
5505
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     5
local encodings = require "util.encodings";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     6
local errors = require "util.error";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     7
local hashes = require "util.hashes";
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     8
local http = require "util.http";
5505
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
     9
local id = require "util.id";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
    10
local it = require "util.iterators";
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    11
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
    12
local json = require "util.json";
5505
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
    13
local schema = require "util.jsonschema";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
    14
local jwt = require "util.jwt";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    15
local random = require "util.random";
5213
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
    16
local set = require "util.set";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    17
local st = require "util.stanza";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    18
5505
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
    19
local base64 = encodings.base64;
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5499
diff changeset
    20
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
    21
local function b64url(s)
5396
c0a6f39caf47 mod_http_oauth2: Fix missing base64 part of base64url (Thanks KeyCloak)
Kim Alvefur <zash@zash.se>
parents: 5395
diff changeset
    22
	return (base64.encode(s):gsub("[+/=]", { ["+"] = "-", ["/"] = "_", ["="] = "" }))
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
    23
end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
    24
5404
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5403
diff changeset
    25
local function tmap(t)
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5403
diff changeset
    26
	return function(k)
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5403
diff changeset
    27
		return t[k];
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5403
diff changeset
    28
	end
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5403
diff changeset
    29
end
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5403
diff changeset
    30
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    31
local function strict_formdecode(query)
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    32
	if not query then
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    33
		return nil;
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    34
	end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    35
	local params = http.formdecode(query);
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    36
	if type(params) ~= "table" then
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    37
		return nil, "no-pairs";
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    38
	end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    39
	local dups = {};
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    40
	for _, pair in ipairs(params) do
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    41
		if dups[pair.name] then
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    42
			return nil, "duplicate";
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    43
		end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    44
		dups[pair.name] = true;
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    45
	end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    46
	return params;
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    47
end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
    48
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    49
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
    50
	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
    51
	if not f then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    52
		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
    53
		if required then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    54
			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
    55
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    56
		return nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    57
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    58
	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
    59
	assert(f:close());
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    60
	return data;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    61
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    62
5558
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
    63
local allowed_locales = module:get_option_array("allowed_oauth2_locales", {});
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
    64
-- TODO Allow translations or per-locale templates somehow.
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
    65
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    66
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
    67
local templates = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    68
	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
    69
	consent = read_file(template_path, "consent.html", true);
5499
7998b49d6512 mod_http_oauth2: Create proper template for OOB code delivery
Kim Alvefur <zash@zash.se>
parents: 5484
diff changeset
    70
	oob = read_file(template_path, "oob.html", true);
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
    71
	device = read_file(template_path, "device.html", true);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    72
	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
    73
	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
    74
	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
    75
};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    76
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    77
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
    78
5551
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
    79
local security_policy = module:get_option_string("oauth2_security_policy", "default-src 'self'");
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
    80
5548
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5530
diff changeset
    81
local render_html = require"util.interpolation".new("%b{}", st.xml_escape);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    82
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
    83
	data = data or {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    84
	data.site_name = site_name;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    85
	local resp = {
5474
40c990159006 mod_http_oauth2: Use error status code when rendering error page
Kim Alvefur <zash@zash.se>
parents: 5473
diff changeset
    86
		status_code = data.error and data.error.code or 200;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    87
		headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    88
			["Content-Type"] = "text/html; charset=utf-8";
5551
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
    89
			["Content-Security-Policy"] = security_policy;
5483
30e2722c9fa3 mod_http_oauth2: Disable Referrer via header
Kim Alvefur <zash@zash.se>
parents: 5482
diff changeset
    90
			["Referrer-Policy"] = "no-referrer";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    91
			["X-Frame-Options"] = "DENY";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    92
			["Cache-Control"] = (sensitive and "no-store" or "no-cache")..", private";
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
    93
			["Pragma"] = "no-cache";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    94
		};
5548
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5530
diff changeset
    95
		body = render_html(template, data);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    96
	};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    97
	return resp;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    98
end
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    99
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   100
local authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   101
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
   102
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
   103
5621
d8622797e315 mod_http_oauth2: Shorten default token validity periods
Kim Alvefur <zash@zash.se>
parents: 5620
diff changeset
   104
local default_access_ttl = module:get_option_number("oauth2_access_token_ttl", 3600);
5623
81042c2a235a mod_http_oauth2: Don't use new time period API just yet
Kim Alvefur <zash@zash.se>
parents: 5622
diff changeset
   105
local default_refresh_ttl = module:get_option_number("oauth2_refresh_token_ttl", 604800);
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   106
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   107
-- 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
   108
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
   109
local registration_algo = module:get_option_string("oauth2_registration_algorithm", "HS256");
5420
2393dbae51ed mod_http_oauth2: Add option for specifying TTL of registered clients
Kim Alvefur <zash@zash.se>
parents: 5413
diff changeset
   110
local registration_ttl = module:get_option("oauth2_registration_ttl", nil);
2393dbae51ed mod_http_oauth2: Add option for specifying TTL of registered clients
Kim Alvefur <zash@zash.se>
parents: 5413
diff changeset
   111
local registration_options = module:get_option("oauth2_registration_options",
2393dbae51ed mod_http_oauth2: Add option for specifying TTL of registered clients
Kim Alvefur <zash@zash.se>
parents: 5413
diff changeset
   112
	{ default_ttl = registration_ttl; accept_expired = not registration_ttl });
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   113
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   114
local pkce_required = module:get_option_boolean("oauth2_require_code_challenge", false);
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   115
5203
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5202
diff changeset
   116
local verification_key;
5463
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5462
diff changeset
   117
local sign_client, verify_client;
5200
6b63af56c8ac mod_http_oauth2: Remove error message
Kim Alvefur <zash@zash.se>
parents: 5199
diff changeset
   118
if registration_key then
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   119
	-- 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
   120
	verification_key = hashes.hmac_sha256(registration_key, module.host);
5463
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5462
diff changeset
   121
	sign_client, verify_client = jwt.init(registration_algo, registration_key, registration_key, registration_options);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   122
end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   123
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   124
local new_device_token, verify_device_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   125
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   126
-- verify and prepare client structure
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   127
local function check_client(client_id)
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   128
	if not verify_client then
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   129
		return nil, "client-registration-not-enabled";
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   130
	end
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   131
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   132
	local ok, client = verify_client(client_id);
5515
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   133
	if not ok then
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   134
		return ok, client;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   135
	end
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   136
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   137
	client.client_hash = b64url(hashes.sha256(client_id));
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   138
	return client;
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   139
end
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   140
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   141
-- scope : string | array | set
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   142
--
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   143
-- at each step, allow the same or a subset of scopes
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   144
-- (all ( client ( grant ( token ) ) ))
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   145
-- preserve order since it determines role if more than one granted
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   146
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   147
-- string -> array
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   148
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
   149
	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
   150
end
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   151
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   152
local openid_claims = set.new();
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   153
module:add_item("openid-claim", "openid");
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   154
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   155
module:handle_items("openid-claim", function(event)
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   156
	authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   157
	openid_claims:add(event.item);
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   158
end, function()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   159
	authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   160
	openid_claims = set.new(module:get_host_items("openid-claim"));
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   161
end, true);
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   162
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   163
-- array -> array, array, array
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   164
local function split_scopes(scope_list)
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   165
	local claims, roles, unknown = array(), array(), array();
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   166
	local all_roles = usermanager.get_all_roles(module.host);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   167
	for _, scope in ipairs(scope_list) do
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   168
		if openid_claims:contains(scope) then
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   169
			claims:push(scope);
5471
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   170
		elseif scope == "xmpp" or all_roles[scope] then
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   171
			roles:push(scope);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   172
		else
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   173
			unknown:push(scope);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   174
		end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   175
	end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   176
	return claims, roles, unknown;
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   177
end
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   178
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   179
local function can_assume_role(username, requested_role)
5471
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   180
	return requested_role == "xmpp" or usermanager.user_can_assume_role(username, module.host, requested_role);
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   181
end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   182
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   183
-- function (string) : function(string) : boolean
5431
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   184
local function role_assumable_by(username)
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   185
	return function(role)
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   186
		return can_assume_role(username, role);
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   187
	end
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   188
end
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   189
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   190
-- string, array --> array
5430
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5429
diff changeset
   191
local function user_assumable_roles(username, requested_roles)
5431
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   192
	return array.filter(requested_roles, role_assumable_by(username));
5430
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5429
diff changeset
   193
end
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5429
diff changeset
   194
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   195
-- string, string|nil --> string, string
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   196
local function filter_scopes(username, requested_scope_string)
5432
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5431
diff changeset
   197
	local requested_scopes, requested_roles = split_scopes(parse_scopes(requested_scope_string or ""));
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   198
5432
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5431
diff changeset
   199
	local granted_roles = user_assumable_roles(username, requested_roles);
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5431
diff changeset
   200
	local granted_scopes = requested_scopes + granted_roles;
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   201
5432
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5431
diff changeset
   202
	local selected_role = granted_roles[1];
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   203
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   204
	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
   205
end
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   206
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   207
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
   208
	return os.difftime(code.expires, os.time());
4673
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
   209
end
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
   210
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   211
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
   212
	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
   213
end
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   214
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   215
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
   216
	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
   217
end);
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   218
5622
869c01d91aea mod_http_oauth2: Clean cache less frequently
Kim Alvefur <zash@zash.se>
parents: 5621
diff changeset
   219
-- Clear out unredeemed codes so they don't linger in memory.
869c01d91aea mod_http_oauth2: Clean cache less frequently
Kim Alvefur <zash@zash.se>
parents: 5621
diff changeset
   220
module:daily("Clear expired authorization codes", function()
4276
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   221
	local k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   222
	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
   223
		codes:set(k, nil);
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   224
		k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   225
	end
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   226
end)
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   227
5211
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   228
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
   229
	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
   230
end
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   231
5462
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   232
-- Non-standard special redirect URI that has the AS show the authorization
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   233
-- code to the user for them to copy-paste into the client, which can then
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   234
-- continue as if it received it via redirect.
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   235
local oob_uri = "urn:ietf:wg:oauth:2.0:oob";
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   236
local device_uri = "urn:ietf:params:oauth:grant-type:device_code";
5462
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   237
5213
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   238
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
   239
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   240
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
   241
	return errors.new({
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   242
		type = "modify";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   243
		condition = "bad-request";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   244
		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
   245
		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
   246
		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
   247
	});
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   248
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   249
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   250
-- 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
   251
-- 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
   252
-- 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
   253
local function client_subset(client)
5515
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   254
	return {
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   255
		name = client.client_name;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   256
		uri = client.client_uri;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   257
		id = client.software_id;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   258
		version = client.software_version;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   259
		hash = client.client_hash;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   260
	};
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   261
end
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   262
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   263
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
   264
	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
   265
	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
   266
		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
   267
	end
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   268
	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
   269
		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
   270
	end
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   271
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   272
	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
   273
	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
   274
		-- 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
   275
		grant = tokens.create_grant(token_jid, token_jid, default_refresh_ttl, token_data);
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   276
	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
   277
5620
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   278
	if refresh_token_info then
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   279
		-- out with the old refresh tokens
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   280
		local ok, err = tokens.revoke_token(refresh_token_info.token);
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   281
		if not ok then
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   282
			module:log("error", "Could not revoke refresh token: %s", err);
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   283
			return 500;
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   284
		end
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   285
	end
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   286
	-- in with the new refresh token
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   287
	local refresh_token = refresh_token_info ~= false and tokens.create_token(token_jid, grant.id, nil, nil, "oauth2-refresh");
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
   288
5471
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   289
	if role == "xmpp" then
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   290
		-- Special scope meaning the users default role.
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   291
		local user_default_role = usermanager.get_user_role(jid.node(token_jid), module.host);
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   292
		role = user_default_role and user_default_role.name;
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   293
	end
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
   294
5455
6705f2a09702 mod_http_oauth2: Reference grant by id instead of value
Kim Alvefur <zash@zash.se>
parents: 5454
diff changeset
   295
	local access_token, access_token_info = tokens.create_token(token_jid, grant.id, role, default_access_ttl, "oauth2");
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   296
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   297
	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
   298
	return {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   299
		token_type = "bearer";
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   300
		access_token = access_token;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   301
		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
   302
		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
   303
		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
   304
		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
   305
	};
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   306
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   307
5465
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   308
local function normalize_loopback(uri)
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   309
	local u = url.parse(uri);
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   310
	if u.scheme == "http" and loopbacks:contains(u.host) then
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   311
		u.authority = nil;
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   312
		u.host = "::1";
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   313
		u.port = nil;
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   314
		return url.build(u);
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   315
	end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   316
	-- else, not a valid loopback uri
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   317
end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   318
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   319
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
   320
	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
   321
		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
   322
			-- 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
   323
			return;
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   324
		end
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   325
		-- 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
   326
		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
   327
	end
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   328
	if query_redirect_uri == device_uri and client.grant_types then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   329
		for _, grant_type in ipairs(client.grant_types) do
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   330
			if grant_type == device_uri then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   331
				return query_redirect_uri;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   332
			end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   333
		end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   334
		-- Tried to use device authorization flow without registering it.
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   335
		return;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   336
	end
5223
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   337
	-- 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
   338
	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
   339
		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
   340
			return redirect_uri
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   341
		end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   342
	end
5465
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   343
	-- The authorization server MUST allow any port to be specified at the time
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   344
	-- of the request for loopback IP redirect URIs, to accommodate clients that
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   345
	-- obtain an available ephemeral port from the operating system at the time
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   346
	-- of the request.
5464
c0d62c1b4424 mod_http_oauth2: Add FIXME about loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5463
diff changeset
   347
	-- https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-08.html#section-8.4.2
5465
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   348
	local loopback_redirect_uri = normalize_loopback(query_redirect_uri);
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   349
	if loopback_redirect_uri then
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   350
		for _, redirect_uri in ipairs(client.redirect_uris) do
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   351
			if loopback_redirect_uri == normalize_loopback(redirect_uri) then
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   352
				return query_redirect_uri;
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   353
			end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   354
		end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   355
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   356
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   357
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   358
local grant_type_handlers = {};
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   359
local response_type_handlers = {};
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   360
local verifier_transforms = {};
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   361
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   362
function grant_type_handlers.implicit()
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   363
	-- Placeholder to make discovery work correctly.
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   364
	-- Access tokens are delivered via redirect when using the implict flow, not
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   365
	-- via the token endpoint, so how did you get here?
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   366
	return oauth_error("invalid_request");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   367
end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   368
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   369
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
   370
	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
   371
	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
   372
	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
   373
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
   374
	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
   375
		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
   376
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   377
	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
   378
		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
   379
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   380
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   381
	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
   382
	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
   383
	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
   384
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   385
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   386
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
   387
	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
   388
	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
   389
		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
   390
	end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   391
	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
   392
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   393
	if pkce_required and not params.code_challenge then
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   394
		return oauth_error("invalid_request", "PKCE required");
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   395
	end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   396
5247
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
   397
	local code = id.medium();
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   398
	if params.redirect_uri == device_uri then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   399
		local is_device, device_state = verify_device_token(params.state);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   400
		if is_device then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   401
			-- reconstruct the device_code
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   402
			code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   403
		end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   404
	end
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   405
	local ok = codes:set("authorization_code:" .. params.client_id .. "#" .. code, {
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   406
		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
   407
		granted_jid = granted_jid;
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   408
		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
   409
		granted_role = granted_role;
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   410
		challenge = params.code_challenge;
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   411
		challenge_method = params.code_challenge_method;
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   412
		id_token = id_token;
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   413
	});
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   414
	if not ok then
5480
575f52b15f5a mod_http_oauth2: Return OAuth error for authz code store error
Kim Alvefur <zash@zash.se>
parents: 5479
diff changeset
   415
		return oauth_error("temporarily_unavailable");
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   416
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   417
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   418
	local redirect_uri = get_redirect_uri(client, params.redirect_uri);
5462
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   419
	if redirect_uri == oob_uri then
5499
7998b49d6512 mod_http_oauth2: Create proper template for OOB code delivery
Kim Alvefur <zash@zash.se>
parents: 5484
diff changeset
   420
		return render_page(templates.oob, { client = client; authorization_code = code }, true);
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   421
	elseif redirect_uri == device_uri then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   422
		return render_page(templates.device, { client = client }, true);
5223
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   423
	elseif not redirect_uri then
5466
f6d8830a83fe mod_http_oauth2: Return proper OAuth error for invalid redirect URI
Kim Alvefur <zash@zash.se>
parents: 5465
diff changeset
   424
		return oauth_error("invalid_redirect_uri");
5192
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   425
	end
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   426
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   427
	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
   428
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   429
	local query = strict_formdecode(redirect.query);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   430
	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
   431
	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
   432
	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
   433
	if params.state then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   434
		table.insert(query, { name = "state", value = params.state });
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   435
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   436
	redirect.query = http.formencode(query);
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
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   439
		status_code = 303;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   440
		headers = {
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   441
			cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   442
			pragma = "no-cache";
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   443
			location = url.build(redirect);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   444
		};
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   445
	}
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   446
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   447
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   448
-- Implicit flow
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   449
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
   450
	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
   451
	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
   452
		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
   453
	end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   454
	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
   455
	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
   456
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   457
	local redirect = url.parse(get_redirect_uri(client, params.redirect_uri));
5467
dacde53467f3 mod_http_oauth2: Proper OAuth error for invalid redirect URI in implicit flow too
Kim Alvefur <zash@zash.se>
parents: 5466
diff changeset
   458
	if not redirect then return oauth_error("invalid_redirect_uri"); end
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   459
	token_info.state = params.state;
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   460
	redirect.fragment = http.formencode(token_info);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   461
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   462
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   463
		status_code = 303;
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   464
		headers = {
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   465
			cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   466
			pragma = "no-cache";
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   467
			location = url.build(redirect);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   468
		};
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   469
	}
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   470
end
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   471
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   472
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
   473
	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
   474
end
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   475
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   476
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
   477
	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
   478
end
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   479
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   480
function grant_type_handlers.authorization_code(params)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   481
	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
   482
	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
   483
	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
   484
	if params.scope and params.scope ~= "" then
5454
d2594bbf7c36 mod_http_oauth2: Scope FIXMEs
Kim Alvefur <zash@zash.se>
parents: 5453
diff changeset
   485
		-- FIXME allow a subset of granted scopes
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   486
		return oauth_error("invalid_scope", "unknown scope requested");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   487
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   488
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   489
	local client = check_client(params.client_id);
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   490
	if not client then
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   491
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   492
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   493
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   494
	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
   495
		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
   496
		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
   497
	end
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   498
	local code, err = codes:get("authorization_code:" .. params.client_id .. "#" .. params.code);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   499
	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
   500
	-- 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
   501
	-- prevent a second attempted use
5554
4fda06be6b08 mod_http_oauth2: Make note about handling repeated
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
   502
	-- TODO if a second attempt *is* made, revoke any tokens issued
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   503
	codes:set("authorization_code:" .. 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
   504
	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
   505
		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
   506
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   507
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   508
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   509
	-- TODO Decide if the code should be removed or not when PKCE fails
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   510
	local transform = verifier_transforms[code.challenge_method or "plain"];
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   511
	if not transform then
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   512
		return oauth_error("invalid_request", "unknown challenge transform method");
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   513
	elseif transform(params.code_verifier) ~= code.challenge then
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   514
		return oauth_error("invalid_grant", "incorrect credentials");
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   515
	end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   516
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   517
	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
   518
end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   519
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   520
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
   521
	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
   522
	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
   523
	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
   524
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   525
	local client = check_client(params.client_id);
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   526
	if not client then
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   527
		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
   528
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   529
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   530
	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
   531
		module:log("debug", "client_secret mismatch");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   532
		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
   533
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   534
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   535
	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
   536
	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
   537
		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
   538
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   539
5516
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   540
	local refresh_token_client = refresh_token_info.grant.data.oauth2_client;
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   541
	if not refresh_token_client.hash or refresh_token_client.hash ~= client.client_hash then
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   542
		module:log("warn", "OAuth client %q (%s) tried to use refresh token belonging to %q (%s)", client.client_name, client.client_hash,
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   543
			refresh_token_client.name, refresh_token_client.hash);
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   544
		return oauth_error("unauthorized_client", "incorrect credentials");
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   545
	end
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   546
5450
dd7bddc87f98 mod_http_oauth2: Fix inclusion of role in refreshed access tokens
Kim Alvefur <zash@zash.se>
parents: 5449
diff changeset
   547
	local refresh_scopes = refresh_token_info.grant.data.oauth2_scopes;
5452
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   548
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   549
	if params.scope then
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   550
		local granted_scopes = set.new(parse_scopes(refresh_scopes));
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   551
		local requested_scopes = parse_scopes(params.scope);
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   552
		refresh_scopes = array.filter(requested_scopes, function(scope)
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   553
			return granted_scopes:contains(scope);
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   554
		end):concat(" ");
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   555
	end
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   556
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   557
	local username = jid.split(refresh_token_info.jid);
5450
dd7bddc87f98 mod_http_oauth2: Fix inclusion of role in refreshed access tokens
Kim Alvefur <zash@zash.se>
parents: 5449
diff changeset
   558
	local new_scopes, role = filter_scopes(username, refresh_scopes);
dd7bddc87f98 mod_http_oauth2: Fix inclusion of role in refreshed access tokens
Kim Alvefur <zash@zash.se>
parents: 5449
diff changeset
   559
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   560
	-- 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
   561
	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
   562
5452
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   563
	return json.encode(new_access_token(refresh_token_info.jid, role, new_scopes, client, nil, refresh_token_info));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   564
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   565
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   566
grant_type_handlers[device_uri] = function(params)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   567
	if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   568
	if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   569
	if not params.device_code then return oauth_error("invalid_request", "missing 'device_code'"); end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   570
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   571
	local client = check_client(params.client_id);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   572
	if not client then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   573
		return oauth_error("invalid_client", "incorrect credentials");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   574
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   575
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   576
	if not verify_client_secret(params.client_id, params.client_secret) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   577
		module:log("debug", "client_secret mismatch");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   578
		return oauth_error("invalid_client", "incorrect credentials");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   579
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   580
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   581
	local code = codes:get("device_code:" .. params.device_code);
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   582
	if type(code) ~= "table" or code_expired(code) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   583
		return oauth_error("expired_token");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   584
	elseif code.error then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   585
		return code.error;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   586
	elseif not code.granted_jid then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   587
		return oauth_error("authorization_pending");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   588
	end
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   589
	codes:set("device_code:" .. params.device_code, nil);
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   590
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   591
	return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   592
end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   593
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   594
-- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   595
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   596
function verifier_transforms.plain(code_verifier)
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   597
	-- code_challenge = code_verifier
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   598
	return code_verifier;
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   599
end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   600
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   601
function verifier_transforms.S256(code_verifier)
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   602
	-- code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
5395
4aedce4fb95d mod_http_oauth2: Fix accidental uppercase in invocation of hash function
Kim Alvefur <zash@zash.se>
parents: 5394
diff changeset
   603
	return code_verifier and b64url(hashes.sha256(code_verifier));
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   604
end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   605
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   606
-- 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
   607
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
   608
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   609
-- 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
   610
-- 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
   611
-- 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
   612
-- (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
   613
--  to one of them).
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   614
-- 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
   615
-- 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
   616
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
   617
	local form = request.method == "POST"
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   618
	         and request.body
5280
67777cb7353d mod_http_oauth2: Pedantic optimization
Kim Alvefur <zash@zash.se>
parents: 5277
diff changeset
   619
	         and request.body ~= ""
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   620
	         and request.headers.content_type == "application/x-www-form-urlencoded"
5518
61b8d3eb91a4 mod_http_oauth2: Revert strict form check to allow consent of multiple scopes
Kim Alvefur <zash@zash.se>
parents: 5517
diff changeset
   621
	         and http.formdecode(request.body);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   622
5281
a1055024b94e mod_http_oauth2: Stricten check of urlencoded form data
Kim Alvefur <zash@zash.se>
parents: 5280
diff changeset
   623
	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
   624
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   625
	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
   626
		-- First step: login
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   627
		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
   628
		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
   629
		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
   630
			return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   631
				error = "Invalid username/password";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   632
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   633
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   634
		return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   635
			user = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   636
				username = username;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   637
				host = module.host;
5523
83ebfc367169 mod_http_oauth2: Return Authentication Time per OpenID Core Section 2
Kim Alvefur <zash@zash.se>
parents: 5522
diff changeset
   638
				token = new_user_token({ username = username; host = module.host; auth_time = os.time() });
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   639
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   640
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   641
	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
   642
		-- Second step: consent
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   643
		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
   644
		if not ok then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   645
			return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   646
				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
   647
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   648
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   649
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   650
		local scopes = array():append(form):filter(function(field)
5428
b45d9a81b3da mod_http_oauth2: Revert role selector, going to try something else
Kim Alvefur <zash@zash.se>
parents: 5427
diff changeset
   651
			return field.name == "scope";
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   652
		end):pluck("value");
5275
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   653
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   654
		user.token = form.user_token;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   655
		return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   656
			user = user;
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   657
			scopes = scopes;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   658
			consent = form.consent == "granted";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   659
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   660
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   661
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   662
	return {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   663
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   664
5226
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   665
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
   666
	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
   667
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   668
	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
   669
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   670
	if auth_type == "Basic" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   671
		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
   672
		if not creds then return; end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   673
		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
   674
		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
   675
		return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   676
			type = "basic";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   677
			username = username;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   678
			password = password;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   679
		};
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   680
	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
   681
		return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   682
			type = "bearer";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   683
			bearer_token = auth_data;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   684
		};
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   685
	end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   686
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   687
	return nil;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   688
end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   689
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   690
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
   691
	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
   692
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   693
	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
   694
		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
   695
		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
   696
		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
   697
		if params.scope then
5454
d2594bbf7c36 mod_http_oauth2: Scope FIXMEs
Kim Alvefur <zash@zash.se>
parents: 5453
diff changeset
   698
			-- TODO shouldn't we support scopes / roles here?
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   699
			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
   700
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   701
		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
   702
			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
   703
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   704
		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
   705
			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
   706
			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
   707
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   708
		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
   709
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   710
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   711
	-- TODO How would this make sense with components?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   712
	-- Have an admin authenticate maybe?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   713
	response_type_handlers.code = nil;
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   714
	response_type_handlers.token = nil;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   715
	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
   716
end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   717
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   718
local function render_error(err)
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   719
	return render_page(templates.error, { error = err });
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   720
end
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   721
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   722
-- 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
   723
-- 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
   724
-- 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
   725
-- 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
   726
-- error directly to the user-agent.
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   727
local function error_response(request, redirect_uri, err)
5482
af105c7a24b2 mod_http_oauth2: Always render errors as HTML for OOB redirect URI
Kim Alvefur <zash@zash.se>
parents: 5481
diff changeset
   728
	if not redirect_uri or redirect_uri == oob_uri then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   729
		return render_error(err);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   730
	end
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   731
	local q = strict_formdecode(request.url.query);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   732
	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
   733
	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
   734
	redirect_uri = redirect_uri
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   735
		.. 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
   736
		.. "&" .. 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
   737
	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
   738
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   739
		status_code = 303;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   740
		headers = {
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   741
			cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   742
			pragma = "no-cache";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   743
			location = redirect_uri;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   744
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   745
	};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   746
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   747
5553
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   748
local allowed_grant_type_handlers = module:get_option_set("allowed_oauth2_grant_types", {
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   749
	"authorization_code";
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   750
	"password"; -- TODO Disable. The resource owner password credentials grant [RFC6749] MUST NOT be used.
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   751
	"refresh_token";
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   752
	device_uri;
5553
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   753
})
5618
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5612
diff changeset
   754
if allowed_grant_type_handlers:contains("device_code") then
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5612
diff changeset
   755
	-- expand short form because that URI is long
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5612
diff changeset
   756
	module:log("debug", "Expanding %q to %q in '%s'", "device_code", device_uri, "allowed_oauth2_grant_types");
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5612
diff changeset
   757
	allowed_grant_type_handlers:remove("device_code");
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5612
diff changeset
   758
	allowed_grant_type_handlers:add(device_uri);
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5612
diff changeset
   759
end
5191
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   760
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
   761
	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
   762
		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
   763
		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
   764
	else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   765
		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
   766
	end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   767
end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   768
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   769
-- "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
   770
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
   771
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
   772
	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
   773
		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
   774
		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
   775
	else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   776
		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
   777
	end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   778
end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   779
5388
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   780
local allowed_challenge_methods = module:get_option_set("allowed_oauth2_code_challenge_methods", { "plain"; "S256" })
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   781
for handler_type in pairs(verifier_transforms) do
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   782
	if not allowed_challenge_methods:contains(handler_type) then
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   783
		module:log("debug", "Challenge method %q disabled", handler_type);
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   784
		verifier_transforms[handler_type] = nil;
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   785
	else
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   786
		module:log("debug", "Challenge method %q enabled", handler_type);
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   787
	end
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   788
end
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   789
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   790
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
   791
	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
   792
3938
469408682152 mod_http_oauth2: Set content type on successful repsponses (fixes #1501)
Kim Alvefur <zash@zash.se>
parents: 3924
diff changeset
   793
	event.response.headers.content_type = "application/json";
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   794
	event.response.headers.cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
   795
	event.response.headers.pragma = "no-cache";
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   796
	local params = strict_formdecode(event.request.body);
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   797
	if not params then
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   798
		return oauth_error("invalid_request", "Could not parse request body as 'application/x-www-form-urlencoded'");
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   799
	end
5227
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   800
5229
3439eb37f23b mod_http_oauth2: token endpoint: handle missing credentials
Matthew Wild <mwild1@gmail.com>
parents: 5228
diff changeset
   801
	if credentials and credentials.type == "basic" then
5389
544b92750a2a mod_http_oauth2: Advertise supported token endpoint auth methods
Kim Alvefur <zash@zash.se>
parents: 5388
diff changeset
   802
		-- client_secret_basic converted internally to client_secret_post
5227
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   803
		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
   804
		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
   805
	end
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   806
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   807
	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
   808
	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
   809
	if not grant_handler then
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   810
		return oauth_error("invalid_request", "No such grant type.");
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   811
	end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   812
	return grant_handler(params);
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   813
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   814
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   815
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
   816
	local request = event.request;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   817
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   818
	-- Directly returning errors to the user before we have a validated client object
4262
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4261
diff changeset
   819
	if not request.url.query then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   820
		return render_error(oauth_error("invalid_request", "Missing query parameters"));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   821
	end
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   822
	local params = strict_formdecode(request.url.query);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   823
	if not params then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   824
		return render_error(oauth_error("invalid_request", "Invalid query parameters"));
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   825
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   826
5475
d4d333cb75b2 mod_http_oauth2: Clarify some error messages
Kim Alvefur <zash@zash.se>
parents: 5474
diff changeset
   827
	if not params.client_id then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   828
		return render_error(oauth_error("invalid_request", "Missing 'client_id' parameter"));
5475
d4d333cb75b2 mod_http_oauth2: Clarify some error messages
Kim Alvefur <zash@zash.se>
parents: 5474
diff changeset
   829
	end
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   830
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   831
	local client = check_client(params.client_id);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   832
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   833
	if not client then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   834
		return render_error(oauth_error("invalid_request", "Invalid 'client_id' parameter"));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   835
	end
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   836
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   837
	local redirect_uri = get_redirect_uri(client, params.redirect_uri);
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   838
	if not redirect_uri then
5479
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5478
diff changeset
   839
		return render_error(oauth_error("invalid_request", "Invalid 'redirect_uri' parameter"));
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5478
diff changeset
   840
	end
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5478
diff changeset
   841
	-- From this point we know that redirect_uri is safe to use
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5478
diff changeset
   842
5409
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   843
	local client_response_types = set.new(array(client.response_types or { "code" }));
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   844
	client_response_types = set.intersection(client_response_types, allowed_response_type_handlers);
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   845
	if not client_response_types:contains(params.response_type) then
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   846
		return error_response(request, redirect_uri, oauth_error("invalid_client", "'response_type' not allowed"));
5409
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   847
	end
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   848
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   849
	local requested_scopes = parse_scopes(params.scope or "");
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   850
	if client.scope then
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   851
		local client_scopes = set.new(parse_scopes(client.scope));
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   852
		requested_scopes:filter(function(scope)
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   853
			return client_scopes:contains(scope);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   854
		end);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   855
	end
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   856
5522
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   857
	-- The 'prompt' parameter from OpenID Core
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   858
	local prompt = set.new(parse_scopes(params.prompt or "select_account login consent"));
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   859
	if prompt:contains("none") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   860
		-- Client wants no interaction, only confirmation of prior login and
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   861
		-- consent, but this is not implemented.
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   862
		return error_response(request, redirect_uri, oauth_error("interaction_required"));
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   863
	elseif not prompt:contains("select_account") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   864
		-- TODO If the login page is split into account selection followed by login
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   865
		-- (e.g. password), and then the account selection could be skipped iff the
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   866
		-- 'login_hint' parameter is present.
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   867
		return error_response(request, redirect_uri, oauth_error("account_selection_required"));
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   868
	elseif not prompt:contains("login") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   869
		-- Currently no cookies or such are used, so login is required every time.
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   870
		return error_response(request, redirect_uri, oauth_error("login_required"));
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   871
	elseif not prompt:contains("consent") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   872
		-- Are there any circumstances when consent would be implied or assumed?
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   873
		return error_response(request, redirect_uri, oauth_error("consent_required"));
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   874
	end
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   875
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   876
	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
   877
	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
   878
		-- Render login page
5470
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   879
		local extra = {};
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   880
		if params.login_hint then
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   881
			extra.username_hint = (jid.prepped_split(params.login_hint));
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   882
		end
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   883
		return render_page(templates.login, { state = auth_state; client = client; extra = extra });
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   884
	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
   885
		-- Render consent page
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   886
		local scopes, roles = split_scopes(requested_scopes);
5456
b071d8ee6555 mod_http_oauth2: Show only roles the user can use in consent dialog
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
   887
		roles = user_assumable_roles(auth_state.user.username, roles);
5433
0bbeee8ba8b5 mod_http_oauth2: Strip unknown scopes from consent page
Kim Alvefur <zash@zash.se>
parents: 5432
diff changeset
   888
		return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   889
	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
   890
		-- Notify client of rejection
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   891
		if redirect_uri == device_uri then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   892
			local is_device, device_state = verify_device_token(params.state);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   893
			if is_device then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   894
				local device_code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   895
				local code = codes:get("device_code:" .. params.client_id .. "#" .. device_code);
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   896
				code.error = oauth_error("access_denied");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   897
				code.expires = os.time() + 60;
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   898
				codes:set("device_code:" .. params.client_id .. "#" .. device_code, code);
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   899
			end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   900
		end
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   901
		return error_response(request, redirect_uri, oauth_error("access_denied"));
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   902
	end
5275
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   903
	-- 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
   904
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   905
	local granted_scopes = auth_state.scopes
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   906
	if client.scope then
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   907
		local client_scopes = set.new(parse_scopes(client.scope));
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   908
		granted_scopes:filter(function(scope)
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   909
			return client_scopes:contains(scope);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   910
		end);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   911
	end
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   912
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   913
	params.scope = granted_scopes:concat(" ");
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   914
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   915
	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
   916
	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
   917
	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
   918
	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
   919
		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
   920
		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
   921
		aud = params.client_id;
5523
83ebfc367169 mod_http_oauth2: Return Authentication Time per OpenID Core Section 2
Kim Alvefur <zash@zash.se>
parents: 5522
diff changeset
   922
		auth_time = auth_state.user.auth_time;
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   923
		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
   924
	});
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   925
	local response_type = params.response_type;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   926
	local response_handler = response_type_handlers[response_type];
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   927
	if not response_handler then
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   928
		return error_response(request, redirect_uri, oauth_error("unsupported_response_type"));
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   929
	end
5472
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
   930
	local ret = response_handler(client, params, user_jid, id_token);
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
   931
	if errors.is_err(ret) then
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   932
		return error_response(request, redirect_uri, ret);
5472
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
   933
	end
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
   934
	return ret;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   935
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   936
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   937
local function handle_device_authorization_request(event)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   938
	local request = event.request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   939
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   940
	local credentials = get_request_credentials(request);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   941
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   942
	local params = strict_formdecode(request.body);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   943
	if not params then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   944
		return render_error(oauth_error("invalid_request", "Invalid query parameters"));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   945
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   946
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   947
	if credentials and credentials.type == "basic" then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   948
		-- client_secret_basic converted internally to client_secret_post
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   949
		params.client_id = http.urldecode(credentials.username);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   950
		local client_secret = http.urldecode(credentials.password);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   951
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   952
		if not verify_client_secret(params.client_id, client_secret) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   953
			module:log("debug", "client_secret mismatch");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   954
			return oauth_error("invalid_client", "incorrect credentials");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   955
		end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   956
	else
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   957
		return 401;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   958
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   959
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   960
	local client = check_client(params.client_id);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   961
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   962
	if not client then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   963
		return render_error(oauth_error("invalid_request", "Invalid 'client_id' parameter"));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   964
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   965
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   966
	if not set.new(client.grant_types):contains(device_uri) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   967
		return render_error(oauth_error("invalid_client", "Client not registered for device authorization grant"));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   968
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   969
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   970
	local requested_scopes = parse_scopes(params.scope or "");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   971
	if client.scope then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   972
		local client_scopes = set.new(parse_scopes(client.scope));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   973
		requested_scopes:filter(function(scope)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   974
			return client_scopes:contains(scope);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   975
		end);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   976
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   977
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   978
	-- TODO better code generator, this one should be easy to type from a
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   979
	-- screen onto a phone
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   980
	local user_code = (id.tiny() .. "-" .. id.tiny()):upper();
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   981
	local collisions = 0;
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   982
	while codes:get("authorization_code:" .. device_uri .. "#" .. user_code) do
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   983
		collisions = collisions + 1;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   984
		if collisions > 10 then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   985
			return oauth_error("temporarily_unavailable");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   986
		end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   987
		user_code = (id.tiny() .. "-" .. id.tiny()):upper();
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   988
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   989
	-- device code should be derivable after consent but not guessable by the user
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   990
	local device_code = b64url(hashes.hmac_sha256(verification_key, user_code));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   991
	local verification_uri = module:http_url() .. "/device";
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   992
	local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   993
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   994
	local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = os.time() + 1200 });
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
   995
	local uc_ok = codes:set("user_code:" .. user_code,
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   996
		{ user_code = user_code; expires = os.time() + 600; client_id = params.client_id;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   997
    scope = requested_scopes:concat(" ") });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   998
	if not dc_ok or not uc_ok then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
   999
		return oauth_error("temporarily_unavailable");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1000
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1001
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1002
	return {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1003
		headers = { content_type = "application/json"; cache_control = "no-store"; pragma = "no-cache" };
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1004
		body = json.encode {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1005
			device_code = device_code;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1006
			user_code = user_code;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1007
			verification_uri = verification_uri;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1008
			verification_uri_complete = verification_uri_complete;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1009
			expires_in = 600;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1010
			interval = 5;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1011
		};
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1012
	}
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1013
end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1014
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1015
local function handle_device_verification_request(event)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1016
	local request = event.request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1017
	local params = strict_formdecode(request.url.query);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1018
	if not params or not params.user_code then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1019
		return render_page(templates.device, { client = false });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1020
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1021
5611
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
  1022
	local device_info = codes:get("user_code:" .. params.user_code);
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5609
diff changeset
  1023
	if not device_info or code_expired(device_info) or not codes:set("user_code:" .. params.user_code, nil) then
5612
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5611
diff changeset
  1024
		return render_page(templates.device, {
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5611
diff changeset
  1025
			client = false;
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5611
diff changeset
  1026
			error = oauth_error("expired_token", "Incorrect or expired code");
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5611
diff changeset
  1027
		});
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1028
	end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1029
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1030
	return {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1031
		status_code = 303;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1032
		headers = {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1033
			location = module:http_url() .. "/authorize" .. "?" .. http.formencode({
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1034
				client_id = device_info.client_id;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1035
				redirect_uri = device_uri;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1036
				response_type = "code";
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1037
				scope = device_info.scope;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1038
				state = new_device_token({ user_code = params.user_code });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1039
			});
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1040
		};
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1041
	}
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1042
end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1043
5630
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1044
local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false);
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1045
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1046
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
  1047
	local request, response = event.request, event.response;
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1048
	response.headers.cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1049
	response.headers.pragma = "no-cache";
5269
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
  1050
	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
  1051
		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
  1052
		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
  1053
			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
  1054
			return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
  1055
		end
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
  1056
		-- OAuth "client" credentials
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
  1057
		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
  1058
			return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
  1059
		end
5630
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1060
		-- TODO check that it's their token I guess?
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1061
	elseif strict_auth_revoke then
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1062
		-- Why require auth to revoke a leaked token?
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1063
		response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5623
diff changeset
  1064
		return 401;
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1065
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1066
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
  1067
	local form_data = strict_formdecode(event.request.body);
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1068
	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
  1069
		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
  1070
		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
  1071
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1072
	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
  1073
	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
  1074
		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
  1075
		return 500;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1076
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1077
	return 200;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1078
end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1079
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1080
local registration_schema = {
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1081
	title = "OAuth 2.0 Dynamic Client Registration Protocol";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1082
	type = "object";
5241
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
  1083
	required = {
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
  1084
		-- 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
  1085
		"client_name";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
  1086
		"client_uri";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
  1087
		-- 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
  1088
		"redirect_uris";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
  1089
	};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1090
	properties = {
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1091
		redirect_uris = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1092
			title = "List of Redirect URIs";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1093
			type = "array";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1094
			minItems = 1;
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1095
			uniqueItems = true;
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1096
			items = { title = "Redirect URI"; type = "string"; format = "uri" };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1097
		};
5381
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5379
diff changeset
  1098
		token_endpoint_auth_method = {
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1099
			title = "Token Endpoint Authentication Method";
5381
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5379
diff changeset
  1100
			type = "string";
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5379
diff changeset
  1101
			enum = { "none"; "client_secret_post"; "client_secret_basic" };
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5379
diff changeset
  1102
			default = "client_secret_basic";
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5379
diff changeset
  1103
		};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1104
		grant_types = {
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1105
			title = "Grant Types";
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
  1106
			type = "array";
5459
80a81e7f3c4e mod_http_oauth2: Require non-empty arrays in client registration
Kim Alvefur <zash@zash.se>
parents: 5458
diff changeset
  1107
			minItems = 1;
5460
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1108
			uniqueItems = true;
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1109
			items = {
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
  1110
				type = "string";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1111
				enum = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1112
					"authorization_code";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1113
					"implicit";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1114
					"password";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1115
					"client_credentials";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1116
					"refresh_token";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1117
					"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
  1118
					"urn:ietf:params:oauth:grant-type:saml2-bearer";
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1119
					device_uri;
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1120
				};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1121
			};
5370
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1122
			default = { "authorization_code" };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1123
		};
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1124
		application_type = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1125
			title = "Application Type";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1126
			description = "Determines which kinds of redirect URIs the client may register. \z
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1127
			The value 'web' limits the client to https:// URLs with the same hostname as in 'client_uri' \z
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1128
			while the value 'native' allows either loopback http:// URLs or application specific URIs.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1129
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1130
			enum = { "native"; "web" };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1131
			default = "web";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1132
		};
5460
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1133
		response_types = {
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1134
			title = "Response Types";
5460
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1135
			type = "array";
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1136
			minItems = 1;
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1137
			uniqueItems = true;
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1138
			items = { type = "string"; enum = { "code"; "token" } };
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1139
			default = { "code" };
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
  1140
		};
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1141
		client_name = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1142
			title = "Client Name";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1143
			description = "Human-readable name of the client, presented to the user in the consent dialog.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1144
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1145
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1146
		client_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1147
			title = "Client URL";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1148
			description = "Should be an link to a page with information about the client.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1149
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1150
			format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1151
			pattern = "^https:";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1152
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1153
		logo_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1154
			title = "Logo URL";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1155
			description = "URL to the clients logotype (not currently used).";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1156
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1157
			format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1158
			pattern = "^https:";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1159
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1160
		scope = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1161
			title = "Scopes";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1162
			description = "Space-separated list of scopes the client promises to restrict itself to.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1163
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1164
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1165
		contacts = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1166
			title = "Contact Addresses";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1167
			description = "Addresses, typically email or URLs where the client developers can be contacted.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1168
			type = "array";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1169
			minItems = 1;
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1170
			items = { type = "string"; format = "email" };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1171
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1172
		tos_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1173
			title = "Terms of Service URL";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1174
			description = "Link to Terms of Service for the client, presented to the user in the consent dialog. \z
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1175
			MUST be a https:// URL with hostname matching that of 'client_uri'.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1176
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1177
			format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1178
			pattern = "^https:";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1179
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1180
		policy_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1181
			title = "Privacy Policy URL";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1182
			description = "Link to a Privacy Policy for the client. MUST be a https:// URL with hostname matching that of 'client_uri'.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1183
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1184
			format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1185
			pattern = "^https:";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1186
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1187
		software_id = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1188
			title = "Software ID";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1189
			description = "Unique identifier for the client software, common for all instances. Typically an UUID.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1190
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1191
			format = "uuid";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1192
		};
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1193
		software_version = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1194
			title = "Software Version";
5609
17aa3bac7f3a mod_http_oauth2: Improve a description in schema
Kim Alvefur <zash@zash.se>
parents: 5602
diff changeset
  1195
			description = "Version of the client software being registered. \z
5602
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1196
			E.g. to allow revoking all related tokens in the event of a security incident.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1197
			type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1198
			example = "2.3.1";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5593
diff changeset
  1199
		};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1200
	};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1201
}
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1202
5558
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1203
-- Limit per-locale fields to allowed locales, partly to keep size of client_id
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1204
-- down, partly because we don't yet use them for anything.
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1205
-- Only relevant for user-visible strings and URIs.
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1206
if allowed_locales[1] then
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1207
	local props = registration_schema.properties;
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1208
	for _, locale in ipairs(allowed_locales) do
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1209
		props["client_name#" .. locale] = props["client_name"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1210
		props["client_uri#" .. locale] = props["client_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1211
		props["logo_uri#" .. locale] = props["logo_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1212
		props["tos_uri#" .. locale] = props["tos_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1213
		props["policy_uri#" .. locale] = props["policy_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1214
	end
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1215
end
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1216
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1217
local function redirect_uri_allowed(redirect_uri, client_uri, app_type)
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1218
	local uri = url.parse(redirect_uri);
5461
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
  1219
	if not uri.scheme then
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
  1220
		return false; -- no relative URLs
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
  1221
	end
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1222
	if app_type == "native" then
5462
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
  1223
		return uri.scheme == "http" and loopbacks:contains(uri.host) or redirect_uri == oob_uri or uri.scheme:find(".", 1, true) ~= nil;
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1224
	elseif app_type == "web" then
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1225
		return uri.scheme == "https" and uri.host == client_uri.host;
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1226
	end
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1227
end
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1228
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1229
function create_client(client_metadata)
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1230
	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
  1231
		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
  1232
	end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1233
5370
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1234
	-- Fill in default values
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1235
	for propname, propspec in pairs(registration_schema.properties) do
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1236
		if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1237
			client_metadata[propname] = propspec.default;
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1238
		end
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1239
	end
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
  1240
5563
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1241
	-- MUST ignore any metadata that it does not understand
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1242
	for propname in pairs(client_metadata) do
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1243
		if not registration_schema.properties[propname] then
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1244
			client_metadata[propname] = nil;
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1245
		end
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1246
	end
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5558
diff changeset
  1247
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
  1248
	local client_uri = url.parse(client_metadata.client_uri);
5405
c8d04ac200fc mod_http_oauth2: Reject loopback URIs as client_uri
Kim Alvefur <zash@zash.se>
parents: 5404
diff changeset
  1249
	if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then
5406
fbf3ede7541b mod_http_oauth2: More appropriate error conditions in client validation
Kim Alvefur <zash@zash.se>
parents: 5405
diff changeset
  1250
		return nil, oauth_error("invalid_client_metadata", "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
  1251
	end
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
  1252
5243
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
  1253
	for _, redirect_uri in ipairs(client_metadata.redirect_uris) do
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
  1254
		if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then
5406
fbf3ede7541b mod_http_oauth2: More appropriate error conditions in client validation
Kim Alvefur <zash@zash.se>
parents: 5405
diff changeset
  1255
			return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI.");
5246
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
  1256
		end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
  1257
	end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
  1258
5248
fa7bd721a3f6 mod_http_oauth2: Fix validation of informative URIs
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
  1259
	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
  1260
		if field ~= "client_uri" and prop_schema.format == "uri" and client_metadata[field] then
5407
c574aaaa4d57 mod_http_oauth2: Simplify validation of various URIs
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
  1261
			if not redirect_uri_allowed(client_metadata[field], client_uri, "web") then
c574aaaa4d57 mod_http_oauth2: Simplify validation of various URIs
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
  1262
				return nil, oauth_error("invalid_client_metadata", "Invalid, insecure or inappropriate informative URI");
5246
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
  1263
			end
5243
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
  1264
		end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
  1265
	end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
  1266
5410
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1267
	local grant_types = set.new(client_metadata.grant_types);
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1268
	local response_types = set.new(client_metadata.response_types);
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1269
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1270
	if grant_types:contains("authorization_code") and not response_types:contains("code") then
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1271
		return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'");
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1272
	elseif grant_types:contains("implicit") and not response_types:contains("token") then
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1273
		return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'");
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1274
	end
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1275
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1276
	if set.intersection(grant_types, allowed_grant_type_handlers):empty() then
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1277
		return nil, oauth_error("invalid_client_metadata", "No allowed 'grant_types' specified");
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1278
	elseif set.intersection(response_types, allowed_response_type_handlers):empty() then
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1279
		return nil, oauth_error("invalid_client_metadata", "No allowed 'response_types' specified");
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1280
	end
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1281
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1282
	-- Do we want to keep everything?
5463
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5462
diff changeset
  1283
	local client_id = sign_client(client_metadata);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1284
5225
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
  1285
	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
  1286
	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
  1287
5411
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1288
	if client_metadata.token_endpoint_auth_method ~= "none" then
5584
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1289
		-- Ensure that each client_id JWT with a client_secret is unique.
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1290
		-- A short ID along with the issued at timestamp should be sufficient to
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1291
		-- rule out brute force attacks.
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1292
		-- Not needed for public clients without a secret, but those are expected
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1293
		-- to be uncommon since they can only do the insecure implicit flow.
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1294
		client_metadata.nonce = id.short();
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1295
feadbd481285 mod_http_oauth2: Only add nonce when issuing a client_secret
Kim Alvefur <zash@zash.se>
parents: 5564
diff changeset
  1296
		local client_secret = make_client_secret(client_id, client_metadata);
5411
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1297
		client_metadata.client_secret = client_secret;
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1298
		client_metadata.client_secret_expires_at = 0;
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1299
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1300
		if not registration_options.accept_expired then
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1301
			client_metadata.client_secret_expires_at = client_metadata.client_id_issued_at + (registration_options.default_ttl or 3600);
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1302
		end
5206
b81fd0d22c66 mod_http_oauth2: Calculate client secret expiry in registration response
Kim Alvefur <zash@zash.se>
parents: 5205
diff changeset
  1303
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1304
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1305
	return client_metadata;
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1306
end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1307
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1308
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
  1309
	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
  1310
	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
  1311
	if err then
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1312
		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
  1313
	end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1314
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1315
	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
  1316
	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
  1317
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1318
	return {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1319
		status_code = 201;
5513
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1320
		headers = {
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1321
			cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1322
			pragma = "no-cache";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1323
			content_type = "application/json";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1324
		};
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1325
		body = json.encode(response);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1326
	};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1327
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1328
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1329
if not registration_key then
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1330
	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
  1331
	handle_authorization_request = nil
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1332
	handle_register_request = nil
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1333
	handle_device_authorization_request = nil
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1334
	handle_device_verification_request = nil
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1335
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1336
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1337
local function handle_userinfo_request(event)
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1338
	local request = event.request;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1339
	local credentials = get_request_credentials(request);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1340
	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
  1341
		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
  1342
		return 401;
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1343
	end
5340
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5339
diff changeset
  1344
	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
  1345
	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
  1346
		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
  1347
		return 403;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1348
	end
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1349
	local scopes = set.new()
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1350
	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
  1351
		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
  1352
	else
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1353
		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
  1354
	end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1355
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1356
	if not scopes:contains("openid") then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1357
		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
  1358
		-- 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
  1359
		return 403;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1360
	end
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1361
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1362
	local user_info = {
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1363
		iss = get_issuer();
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1364
		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
  1365
	}
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1366
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1367
	local token_claims = set.intersection(openid_claims, scopes);
5379
8b7d97f0ae8a mod_http_oauth2: Fix to include "openid" scope in discovery metadata
Kim Alvefur <zash@zash.se>
parents: 5371
diff changeset
  1368
	token_claims:remove("openid"); -- that's "iss" and "sub" above
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1369
	if not token_claims:empty() then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1370
		-- Another module can do that
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1371
		module:fire_event("token/userinfo", {
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1372
			token = token_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1373
			claims = token_claims;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1374
			username = jid.split(token_info.jid);
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1375
			userinfo = user_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1376
		});
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1377
	end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1378
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1379
	return {
5262
9629971e307f mod_http_oauth2: Fix userinfo status code off-by-one
Kim Alvefur <zash@zash.se>
parents: 5261
diff changeset
  1380
		status_code = 200;
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1381
		headers = { content_type = "application/json" };
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1382
		body = json.encode(user_info);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1383
	};
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1384
end
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1385
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1386
module:depends("http");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1387
module:provides("http", {
5484
5108f63e762b mod_http_oauth2: Allow CORS for browser clients
Kim Alvefur <zash@zash.se>
parents: 5483
diff changeset
  1388
	cors = { enabled = true; credentials = true };
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1389
	route = {
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1390
		-- OAuth 2.0 in 5 simple steps!
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1391
		-- This is the normal 'authorization_code' flow.
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1392
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1393
		-- Step 1. Create OAuth client
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1394
		["POST /register"] = handle_register_request;
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1395
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1396
		-- Device flow
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1397
		["POST /device"] = handle_device_authorization_request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1398
		["GET /device"] = handle_device_verification_request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1399
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1400
		-- Step 2. User-facing login and consent view
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
  1401
		["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
  1402
		["POST /authorize"] = handle_authorization_request;
5552
fd3c12c40cd9 mod_http_oauth2: Disable CORS for authorization endpoint
Kim Alvefur <zash@zash.se>
parents: 5551
diff changeset
  1403
		["OPTIONS /authorize"] = { status_code = 403; body = "" };
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1404
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1405
		-- Step 3. User is redirected to the 'redirect_uri' along with an
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1406
		-- authorization code.  In the insecure 'implicit' flow, the access token
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1407
		-- is delivered here.
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1408
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1409
		-- Step 4. Retrieve access token using the code.
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1410
		["POST /token"] = handle_token_grant;
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1411
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1412
		-- Step 4 is later repeated using the refresh token to get new access tokens.
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1413
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1414
		-- Step 5. Revoke token (access or refresh)
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
  1415
		["POST /revoke"] = handle_revocation_request;
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1416
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1417
		-- OpenID
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1418
		["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
  1419
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1420
		-- Optional static content for templates
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1421
		["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
  1422
			headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1423
				["Content-Type"] = "text/css";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1424
			};
5548
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5530
diff changeset
  1425
			body = render_html(templates.css, module:get_option("oauth2_template_style"));
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1426
		} or nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1427
		["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
  1428
			headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1429
				["Content-Type"] = "text/javascript";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1430
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1431
			body = templates.js;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1432
		} or nil;
5397
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5396
diff changeset
  1433
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5396
diff changeset
  1434
		-- Some convenient fallback handlers
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5396
diff changeset
  1435
		["GET /register"] = { headers = { content_type = "application/schema+json" }; body = json.encode(registration_schema) };
5400
ac7c5669e5f5 mod_http_oauth2: Return status 405 for GET to endpoints without GET handler
Kim Alvefur <zash@zash.se>
parents: 5398
diff changeset
  1436
		["GET /token"] = function() return 405; end;
ac7c5669e5f5 mod_http_oauth2: Return status 405 for GET to endpoints without GET handler
Kim Alvefur <zash@zash.se>
parents: 5398
diff changeset
  1437
		["GET /revoke"] = function() return 405; end;
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1438
	};
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1439
});
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1440
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1441
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
  1442
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1443
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
  1444
	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
  1445
	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
  1446
		return;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1447
	end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1448
	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
  1449
	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
  1450
	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
  1451
end, 5);
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1452
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1453
-- OIDC Discovery
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1454
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1455
function get_authorization_server_metadata()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1456
	if authorization_server_metadata then
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1457
		return authorization_server_metadata;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1458
	end
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1459
	authorization_server_metadata = {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1460
		-- RFC 8414: OAuth 2.0 Authorization Server Metadata
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1461
		issuer = get_issuer();
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1462
		authorization_endpoint = handle_authorization_request and module:http_url() .. "/authorize" or nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1463
		token_endpoint = handle_token_grant and module:http_url() .. "/token" or nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1464
		registration_endpoint = handle_register_request and module:http_url() .. "/register" or nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1465
		scopes_supported = usermanager.get_all_roles
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1466
			and array(it.keys(usermanager.get_all_roles(module.host))):push("xmpp"):append(array(openid_claims:items()));
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1467
		response_types_supported = array(it.keys(response_type_handlers));
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1468
		token_endpoint_auth_methods_supported = array({ "client_secret_post"; "client_secret_basic" });
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1469
		op_policy_uri = module:get_option_string("oauth2_policy_url", nil);
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1470
		op_tos_uri = module:get_option_string("oauth2_terms_url", nil);
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1471
		revocation_endpoint = handle_revocation_request and module:http_url() .. "/revoke" or nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1472
		revocation_endpoint_auth_methods_supported = array({ "client_secret_basic" });
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1473
		device_authorization_endpoint = handle_device_authorization_request and module:http_url() .. "/device";
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1474
		code_challenge_methods_supported = array(it.keys(verifier_transforms));
5593
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5584
diff changeset
  1475
		grant_types_supported = array(it.keys(grant_type_handlers));
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1476
		response_modes_supported = array(it.keys(response_type_handlers)):map(tmap { token = "fragment"; code = "query" });
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1477
		authorization_response_iss_parameter_supported = true;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1478
		service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html");
5558
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5557
diff changeset
  1479
		ui_locales_supported = allowed_locales[1] and allowed_locales;
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1480
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1481
		-- OpenID
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1482
		userinfo_endpoint = handle_register_request and module:http_url() .. "/userinfo" or nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1483
		jwks_uri = nil; -- REQUIRED in OpenID Discovery but not in OAuth 2.0 Metadata
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1484
		id_token_signing_alg_values_supported = { "HS256" }; -- The algorithm RS256 MUST be included, but we use HS256 and client_secret as shared key.
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1485
	}
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1486
	return authorization_server_metadata;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1487
end
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1488
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1489
module:provides("http", {
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1490
	name = "oauth2-discovery";
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1491
	default_path = "/.well-known/oauth-authorization-server";
5484
5108f63e762b mod_http_oauth2: Allow CORS for browser clients
Kim Alvefur <zash@zash.se>
parents: 5483
diff changeset
  1492
	cors = { enabled = true };
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1493
	route = {
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1494
		["GET"] = function()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1495
			return {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1496
				headers = { content_type = "application/json" };
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1497
				body = json.encode(get_authorization_server_metadata());
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1498
			}
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1499
		end
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1500
	};
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1501
});
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1502
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1503
module:shared("tokenauth/oauthbearer_config").oidc_discovery_url = module:http_url("oauth2-discovery", "/.well-known/oauth-authorization-server");