mod_http_oauth2/mod_http_oauth2.lua
author Kim Alvefur <zash@zash.se>
Fri, 16 Jun 2023 00:06:53 +0200
changeset 5553 01a0b67a9afd
parent 5552 fd3c12c40cd9
child 5554 4fda06be6b08
permissions -rw-r--r--
mod_http_oauth2: Add TODO about disabling password grant Per recommendation in draft-ietf-oauth-security-topics-23 it should at the very least be disabled by default. However since this is used by the Snikket web portal some care needs to be taken not to break this, unless it's already broken by other changes to this module.
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
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    63
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
    64
local templates = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    65
	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
    66
	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
    67
	oob = read_file(template_path, "oob.html", true);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    68
	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
    69
	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
    70
	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
    71
};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    72
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    73
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
    74
5551
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
    75
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
    76
5548
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5530
diff changeset
    77
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
    78
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
    79
	data = data or {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    80
	data.site_name = site_name;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    81
	local resp = {
5474
40c990159006 mod_http_oauth2: Use error status code when rendering error page
Kim Alvefur <zash@zash.se>
parents: 5473
diff changeset
    82
		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
    83
		headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    84
			["Content-Type"] = "text/html; charset=utf-8";
5551
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
    85
			["Content-Security-Policy"] = security_policy;
5483
30e2722c9fa3 mod_http_oauth2: Disable Referrer via header
Kim Alvefur <zash@zash.se>
parents: 5482
diff changeset
    86
			["Referrer-Policy"] = "no-referrer";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    87
			["X-Frame-Options"] = "DENY";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    88
			["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
    89
			["Pragma"] = "no-cache";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    90
		};
5548
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5530
diff changeset
    91
		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
    92
	};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    93
	return resp;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
    94
end
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    95
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
    96
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
    97
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
    98
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
    99
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   100
local default_access_ttl = module:get_option_number("oauth2_access_token_ttl", 86400);
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   101
local default_refresh_ttl = module:get_option_number("oauth2_refresh_token_ttl", nil);
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   102
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   103
-- 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
   104
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
   105
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
   106
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
   107
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
   108
	{ 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
   109
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   110
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
   111
5203
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5202
diff changeset
   112
local verification_key;
5463
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5462
diff changeset
   113
local sign_client, verify_client;
5200
6b63af56c8ac mod_http_oauth2: Remove error message
Kim Alvefur <zash@zash.se>
parents: 5199
diff changeset
   114
if registration_key then
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   115
	-- 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
   116
	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
   117
	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
   118
end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   119
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   120
-- verify and prepare client structure
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   121
local function check_client(client_id)
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   122
	if not verify_client then
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   123
		return nil, "client-registration-not-enabled";
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   124
	end
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   125
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   126
	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
   127
	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
   128
		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
   129
	end
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   130
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   131
	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
   132
	return client;
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   133
end
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   134
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   135
-- scope : string | array | set
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   136
--
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   137
-- 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
   138
-- (all ( client ( grant ( token ) ) ))
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   139
-- 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
   140
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   141
-- 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
   142
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
   143
	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
   144
end
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   145
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   146
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
   147
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
   148
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   149
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
   150
	authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   151
	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
   152
end, function()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   153
	authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
   154
	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
   155
end, true);
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
   156
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   157
-- array -> array, array, array
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   158
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
   159
	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
   160
	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
   161
	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
   162
		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
   163
			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
   164
		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
   165
			roles:push(scope);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   166
		else
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   167
			unknown:push(scope);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   168
		end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   169
	end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   170
	return claims, roles, unknown;
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   171
end
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   172
5421
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   173
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
   174
	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
   175
end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5420
diff changeset
   176
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   177
-- function (string) : function(string) : boolean
5431
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   178
local function role_assumable_by(username)
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   179
	return function(role)
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   180
		return can_assume_role(username, role);
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   181
	end
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   182
end
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5430
diff changeset
   183
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   184
-- string, array --> array
5430
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5429
diff changeset
   185
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
   186
	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
   187
end
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5429
diff changeset
   188
5453
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5452
diff changeset
   189
-- 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
   190
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
   191
	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
   192
5432
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5431
diff changeset
   193
	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
   194
	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
   195
5432
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5431
diff changeset
   196
	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
   197
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   198
	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
   199
end
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   200
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   201
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
   202
	return os.difftime(code.expires, os.time());
4673
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
   203
end
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4374
diff changeset
   204
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   205
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
   206
	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
   207
end
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   208
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   209
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
   210
	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
   211
end);
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   212
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   213
-- Periodically clear out unredeemed codes.  Does not need to be exact, expired
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   214
-- codes are rejected if tried. Mostly just to keep memory usage in check.
5358
39d59d857bfb mod_http_oauth2: Use new mod_cron API for periodic cleanup
Kim Alvefur <zash@zash.se>
parents: 5345
diff changeset
   215
module:hourly("Clear expired authorization codes", function()
4276
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   216
	local k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   217
	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
   218
		codes:set(k, nil);
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   219
		k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   220
	end
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   221
end)
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4275
diff changeset
   222
5211
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   223
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
   224
	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
   225
end
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5210
diff changeset
   226
5462
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   227
-- 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
   228
-- 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
   229
-- 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
   230
local oob_uri = "urn:ietf:wg:oauth:2.0:oob";
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
   231
5213
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5212
diff changeset
   232
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
   233
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   234
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
   235
	return errors.new({
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   236
		type = "modify";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   237
		condition = "bad-request";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   238
		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
   239
		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
   240
		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
   241
	});
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   242
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   243
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   244
-- 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
   245
-- 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
   246
-- 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
   247
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
   248
	return {
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
   249
		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
   250
		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
   251
		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
   252
		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
   253
		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
   254
	};
5252
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   255
end
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5251
diff changeset
   256
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   257
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
   258
	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
   259
	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
   260
		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
   261
	end
5258
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
   262
	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
   263
		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
   264
	end
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   265
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   266
	local refresh_token;
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   267
	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
   268
	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
   269
		-- 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
   270
		grant = tokens.create_grant(token_jid, token_jid, default_refresh_ttl, token_data);
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   271
		-- Create refresh token for the grant if desired
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   272
		refresh_token = refresh_token_info ~= false and tokens.create_token(token_jid, grant, nil, nil, "oauth2-refresh");
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   273
	else
5284
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   274
		-- Grant exists, reuse existing refresh token
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5283
diff changeset
   275
		refresh_token = refresh_token_info.token;
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
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
   278
	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
   279
		-- 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
   280
		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
   281
		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
   282
	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
   283
5455
6705f2a09702 mod_http_oauth2: Reference grant by id instead of value
Kim Alvefur <zash@zash.se>
parents: 5454
diff changeset
   284
	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
   285
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   286
	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
   287
	return {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   288
		token_type = "bearer";
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   289
		access_token = access_token;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   290
		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
   291
		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
   292
		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
   293
		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
   294
	};
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   295
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   296
5465
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   297
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
   298
	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
   299
	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
   300
		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
   301
		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
   302
		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
   303
		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
   304
	end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   305
	-- 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
   306
end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   307
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   308
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
   309
	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
   310
		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
   311
			-- 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
   312
			return;
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   313
		end
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   314
		-- 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
   315
		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
   316
	end
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   317
	-- 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
   318
	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
   319
		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
   320
			return redirect_uri
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   321
		end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   322
	end
5465
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   323
	-- 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
   324
	-- 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
   325
	-- 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
   326
	-- of the request.
5464
c0d62c1b4424 mod_http_oauth2: Add FIXME about loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5463
diff changeset
   327
	-- 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
   328
	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
   329
	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
   330
		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
   331
			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
   332
				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
   333
			end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   334
		end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5464
diff changeset
   335
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   336
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   337
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   338
local grant_type_handlers = {};
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   339
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
   340
local verifier_transforms = {};
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   341
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   342
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
   343
	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
   344
	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
   345
	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
   346
3912
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3907
diff changeset
   347
	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
   348
		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
   349
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   350
	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
   351
		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
   352
	end
4344
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   353
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   354
	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
   355
	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
   356
	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
   357
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   358
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   359
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
   360
	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
   361
	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
   362
		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
   363
	end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   364
	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
   365
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   366
	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
   367
		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
   368
	end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   369
5247
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
   370
	local code = id.medium();
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   371
	local ok = codes:set(params.client_id .. "#" .. code, {
5217
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5214
diff changeset
   372
		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
   373
		granted_jid = granted_jid;
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4280
diff changeset
   374
		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
   375
		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
   376
		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
   377
		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
   378
		id_token = id_token;
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   379
	});
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   380
	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
   381
		return oauth_error("temporarily_unavailable");
4674
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4673
diff changeset
   382
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   383
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   384
	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
   385
	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
   386
		return render_page(templates.oob, { client = client; authorization_code = code }, true);
5223
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
   387
	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
   388
		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
   389
	end
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   390
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5191
diff changeset
   391
	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
   392
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   393
	local query = strict_formdecode(redirect.query);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   394
	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
   395
	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
   396
	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
   397
	if params.state then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   398
		table.insert(query, { name = "state", value = params.state });
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   399
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   400
	redirect.query = http.formencode(query);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   401
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   402
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   403
		status_code = 303;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   404
		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
   405
			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
   406
			pragma = "no-cache";
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   407
			location = url.build(redirect);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   408
		};
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   409
	}
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   410
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   411
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   412
-- Implicit flow
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   413
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
   414
	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
   415
	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
   416
		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
   417
	end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
   418
	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
   419
	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
   420
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   421
	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
   422
	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
   423
	token_info.state = params.state;
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   424
	redirect.fragment = http.formencode(token_info);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   425
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   426
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   427
		status_code = 303;
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   428
		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
   429
			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
   430
			pragma = "no-cache";
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   431
			location = url.build(redirect);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   432
		};
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   433
	}
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   434
end
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   435
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   436
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
   437
	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
   438
end
4267
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   439
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   440
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
   441
	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
   442
end
d3af5f94d6df mod_http_oauth2: Improve storage of client secret
Kim Alvefur <zash@zash.se>
parents: 4264
diff changeset
   443
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   444
function grant_type_handlers.authorization_code(params)
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   445
	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
   446
	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
   447
	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
   448
	if params.scope and params.scope ~= "" then
5454
d2594bbf7c36 mod_http_oauth2: Scope FIXMEs
Kim Alvefur <zash@zash.se>
parents: 5453
diff changeset
   449
		-- FIXME allow a subset of granted scopes
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   450
		return oauth_error("invalid_scope", "unknown scope requested");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   451
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   452
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   453
	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
   454
	if not client then
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   455
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   456
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   457
5266
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5263
diff changeset
   458
	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
   459
		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
   460
		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
   461
	end
4275
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4274
diff changeset
   462
	local code, err = codes:get(params.client_id .. "#" .. params.code);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   463
	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
   464
	-- 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
   465
	-- prevent a second attempted use
d5492bc861f6 mod_http_oauth2: Remove authorization codes after use
Kim Alvefur <zash@zash.se>
parents: 5217
diff changeset
   466
	codes:set(params.client_id .. "#" .. params.code, nil);
4273
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4269
diff changeset
   467
	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
   468
		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
   469
		return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   470
	end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   471
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   472
	-- 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
   473
	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
   474
	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
   475
		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
   476
	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
   477
		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
   478
	end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   479
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   480
	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
   481
end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   482
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   483
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
   484
	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
   485
	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
   486
	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
   487
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   488
	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
   489
	if not client then
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   490
		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
   491
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   492
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   493
	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
   494
		module:log("debug", "client_secret mismatch");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   495
		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
   496
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   497
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   498
	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
   499
	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
   500
		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
   501
	end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   502
5516
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   503
	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
   504
	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
   505
		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
   506
			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
   507
		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
   508
	end
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5515
diff changeset
   509
5450
dd7bddc87f98 mod_http_oauth2: Fix inclusion of role in refreshed access tokens
Kim Alvefur <zash@zash.se>
parents: 5449
diff changeset
   510
	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
   511
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   512
	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
   513
		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
   514
		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
   515
		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
   516
			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
   517
		end):concat(" ");
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   518
	end
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   519
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   520
	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
   521
	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
   522
5283
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5282
diff changeset
   523
	-- 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
   524
	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
   525
5452
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
   526
	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
   527
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   528
5387
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   529
-- 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
   530
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   531
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
   532
	-- 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
   533
	return code_verifier;
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   534
end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   535
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   536
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
   537
	-- 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
   538
	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
   539
end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5386
diff changeset
   540
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   541
-- 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
   542
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
   543
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   544
-- 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
   545
-- 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
   546
-- 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
   547
-- (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
   548
--  to one of them).
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   549
-- 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
   550
-- 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
   551
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
   552
	local form = request.method == "POST"
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   553
	         and request.body
5280
67777cb7353d mod_http_oauth2: Pedantic optimization
Kim Alvefur <zash@zash.se>
parents: 5277
diff changeset
   554
	         and request.body ~= ""
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   555
	         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
   556
	         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
   557
5281
a1055024b94e mod_http_oauth2: Stricten check of urlencoded form data
Kim Alvefur <zash@zash.se>
parents: 5280
diff changeset
   558
	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
   559
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   560
	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
   561
		-- First step: login
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   562
		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
   563
		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
   564
		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
   565
			return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   566
				error = "Invalid username/password";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   567
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   568
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   569
		return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   570
			user = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   571
				username = username;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   572
				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
   573
				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
   574
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   575
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   576
	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
   577
		-- Second step: consent
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   578
		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
   579
		if not ok then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   580
			return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   581
				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
   582
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   583
		end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   584
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   585
		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
   586
			return field.name == "scope";
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   587
		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
   588
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   589
		user.token = form.user_token;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   590
		return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   591
			user = user;
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   592
			scopes = scopes;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   593
			consent = form.consent == "granted";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   594
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   595
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   596
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   597
	return {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   598
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   599
5226
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   600
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
   601
	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
   602
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   603
	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
   604
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   605
	if auth_type == "Basic" then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   606
		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
   607
		if not creds then return; end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   608
		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
   609
		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
   610
		return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   611
			type = "basic";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   612
			username = username;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   613
			password = password;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   614
		};
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   615
	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
   616
		return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   617
			type = "bearer";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   618
			bearer_token = auth_data;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   619
		};
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   620
	end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   621
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   622
	return nil;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   623
end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5225
diff changeset
   624
3924
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   625
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
   626
	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
   627
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   628
	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
   629
		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
   630
		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
   631
		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
   632
		if params.scope then
5454
d2594bbf7c36 mod_http_oauth2: Scope FIXMEs
Kim Alvefur <zash@zash.se>
parents: 5453
diff changeset
   633
			-- 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
   634
			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
   635
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   636
		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
   637
			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
   638
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   639
		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
   640
			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
   641
			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
   642
		end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   643
		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
   644
	end
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   645
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   646
	-- TODO How would this make sense with components?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   647
	-- Have an admin authenticate maybe?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   648
	response_type_handlers.code = nil;
5190
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5189
diff changeset
   649
	response_type_handlers.token = nil;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   650
	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
   651
end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3923
diff changeset
   652
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   653
local function render_error(err)
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   654
	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
   655
end
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   656
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   657
-- 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
   658
-- 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
   659
-- 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
   660
-- 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
   661
-- 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
   662
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
   663
	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
   664
		return render_error(err);
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   665
	end
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   666
	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
   667
	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
   668
	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
   669
	redirect_uri = redirect_uri
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   670
		.. 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
   671
		.. "&" .. 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
   672
	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
   673
	return {
5214
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5213
diff changeset
   674
		status_code = 303;
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   675
		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
   676
			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
   677
			pragma = "no-cache";
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   678
			location = redirect_uri;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   679
		};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   680
	};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   681
end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   682
5553
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   683
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
   684
	"authorization_code";
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   685
	"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
   686
	"refresh_token";
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5552
diff changeset
   687
})
5191
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   688
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
   689
	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
   690
		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
   691
		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
   692
	else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   693
		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
   694
	end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   695
end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   696
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   697
-- "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
   698
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
   699
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
   700
	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
   701
		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
   702
		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
   703
	else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5233
diff changeset
   704
		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
   705
	end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   706
end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
   707
5388
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   708
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
   709
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
   710
	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
   711
		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
   712
		verifier_transforms[handler_type] = nil;
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   713
	else
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   714
		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
   715
	end
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   716
end
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5387
diff changeset
   717
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   718
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
   719
	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
   720
3938
469408682152 mod_http_oauth2: Set content type on successful repsponses (fixes #1501)
Kim Alvefur <zash@zash.se>
parents: 3924
diff changeset
   721
	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
   722
	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
   723
	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
   724
	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
   725
	if not params then
5478
d0b93105b289 mod_http_oauth2: Don't return redirects or HTML from token endpoint
Kim Alvefur <zash@zash.se>
parents: 5477
diff changeset
   726
		return oauth_error("invalid_request");
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   727
	end
5227
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   728
5229
3439eb37f23b mod_http_oauth2: token endpoint: handle missing credentials
Matthew Wild <mwild1@gmail.com>
parents: 5228
diff changeset
   729
	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
   730
		-- 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
   731
		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
   732
		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
   733
	end
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5226
diff changeset
   734
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   735
	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
   736
	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
   737
	if not grant_handler then
5478
d0b93105b289 mod_http_oauth2: Don't return redirects or HTML from token endpoint
Kim Alvefur <zash@zash.se>
parents: 5477
diff changeset
   738
		return oauth_error("invalid_request");
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   739
	end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   740
	return grant_handler(params);
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   741
end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   742
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   743
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
   744
	local request = event.request;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   745
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   746
	-- 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
   747
	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
   748
		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
   749
	end
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   750
	local params = strict_formdecode(request.url.query);
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   751
	if not params then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   752
		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
   753
	end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   754
5475
d4d333cb75b2 mod_http_oauth2: Clarify some error messages
Kim Alvefur <zash@zash.se>
parents: 5474
diff changeset
   755
	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
   756
		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
   757
	end
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   758
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   759
	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
   760
5514
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
   761
	if not client then
5476
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
   762
		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
   763
	end
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   764
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   765
	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
   766
	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
   767
		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
   768
	end
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5478
diff changeset
   769
	-- 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
   770
5409
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   771
	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
   772
	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
   773
	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
   774
		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
   775
	end
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5408
diff changeset
   776
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   777
	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
   778
	if client.scope then
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   779
		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
   780
		requested_scopes:filter(function(scope)
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   781
			return client_scopes:contains(scope);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   782
		end);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   783
	end
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   784
5522
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   785
	-- The 'prompt' parameter from OpenID Core
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   786
	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
   787
	if prompt:contains("none") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   788
		-- 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
   789
		-- consent, but this is not implemented.
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   790
		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
   791
	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
   792
		-- 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
   793
		-- (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
   794
		-- 'login_hint' parameter is present.
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   795
		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
   796
	elseif not prompt:contains("login") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   797
		-- 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
   798
		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
   799
	elseif not prompt:contains("consent") then
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   800
		-- 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
   801
		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
   802
	end
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5518
diff changeset
   803
5212
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
   804
	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
   805
	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
   806
		-- 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
   807
		local extra = {};
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   808
		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
   809
			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
   810
		end
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
   811
		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
   812
	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
   813
		-- Render consent page
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   814
		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
   815
		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
   816
		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
   817
	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
   818
		-- Notify client of rejection
5481
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
   819
		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
   820
	end
5275
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5272
diff changeset
   821
	-- 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
   822
5451
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   823
	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
   824
	if client.scope then
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   825
		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
   826
		granted_scopes:filter(function(scope)
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   827
			return client_scopes:contains(scope);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   828
		end);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   829
	end
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   830
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
   831
	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
   832
5261
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5260
diff changeset
   833
	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
   834
	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
   835
	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
   836
	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
   837
		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
   838
		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
   839
		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
   840
		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
   841
		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
   842
	});
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   843
	local response_type = params.response_type;
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   844
	local response_handler = response_type_handlers[response_type];
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   845
	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
   846
		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
   847
	end
5472
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
   848
	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
   849
	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
   850
		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
   851
	end
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
   852
	return ret;
4260
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   853
end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4241
diff changeset
   854
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   855
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
   856
	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
   857
	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
   858
	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
   859
	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
   860
		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
   861
		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
   862
			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
   863
			return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   864
		end
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   865
		-- OAuth "client" credentials
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   866
		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
   867
			return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
   868
		end
4374
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   869
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   870
5517
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5516
diff changeset
   871
	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
   872
	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
   873
		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
   874
		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
   875
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   876
	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
   877
	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
   878
		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
   879
		return 500;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   880
	end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   881
	return 200;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   882
end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4344
diff changeset
   883
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   884
local registration_schema = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   885
	type = "object";
5241
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   886
	required = {
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   887
		-- 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
   888
		"client_name";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   889
		"client_uri";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   890
		-- 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
   891
		"redirect_uris";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5240
diff changeset
   892
	};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   893
	properties = {
5458
6970c73711c2 mod_http_oauth2: Reject duplicate redirect URIs in registration
Kim Alvefur <zash@zash.se>
parents: 5457
diff changeset
   894
		redirect_uris = { type = "array"; minItems = 1; uniqueItems = true; items = { type = "string"; format = "uri" } };
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
   895
		token_endpoint_auth_method = {
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5379
diff changeset
   896
			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
   897
			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
   898
			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
   899
		};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   900
		grant_types = {
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   901
			type = "array";
5459
80a81e7f3c4e mod_http_oauth2: Require non-empty arrays in client registration
Kim Alvefur <zash@zash.se>
parents: 5458
diff changeset
   902
			minItems = 1;
5460
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   903
			uniqueItems = true;
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   904
			items = {
5240
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5235
diff changeset
   905
				type = "string";
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   906
				enum = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   907
					"authorization_code";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   908
					"implicit";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   909
					"password";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   910
					"client_credentials";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   911
					"refresh_token";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   912
					"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
   913
					"urn:ietf:params:oauth:grant-type:saml2-bearer";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   914
				};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   915
			};
5370
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
   916
			default = { "authorization_code" };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   917
		};
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
   918
		application_type = { type = "string"; enum = { "native"; "web" }; default = "web" };
5460
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   919
		response_types = {
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   920
			type = "array";
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   921
			minItems = 1;
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   922
			uniqueItems = true;
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   923
			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
   924
			default = { "code" };
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
   925
		};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   926
		client_name = { type = "string" };
5363
230fc6a0c086 mod_http_oauth2: Use new Lua pattern schema properties
Kim Alvefur <zash@zash.se>
parents: 5362
diff changeset
   927
		client_uri = { type = "string"; format = "uri"; luaPattern = "^https:" };
5368
0444953e3247 mod_http_oauth2: Normalize whitespace in client metadata schema
Kim Alvefur <zash@zash.se>
parents: 5363
diff changeset
   928
		logo_uri = { type = "string"; format = "uri"; luaPattern = "^https:" };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   929
		scope = { type = "string" };
5459
80a81e7f3c4e mod_http_oauth2: Require non-empty arrays in client registration
Kim Alvefur <zash@zash.se>
parents: 5458
diff changeset
   930
		contacts = { type = "array"; minItems = 1; items = { type = "string"; format = "email" } };
5368
0444953e3247 mod_http_oauth2: Normalize whitespace in client metadata schema
Kim Alvefur <zash@zash.se>
parents: 5363
diff changeset
   931
		tos_uri = { type = "string"; format = "uri"; luaPattern = "^https:" };
0444953e3247 mod_http_oauth2: Normalize whitespace in client metadata schema
Kim Alvefur <zash@zash.se>
parents: 5363
diff changeset
   932
		policy_uri = { type = "string"; format = "uri"; luaPattern = "^https:" };
0444953e3247 mod_http_oauth2: Normalize whitespace in client metadata schema
Kim Alvefur <zash@zash.se>
parents: 5363
diff changeset
   933
		jwks_uri = { type = "string"; format = "uri"; luaPattern = "^https:" };
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   934
		jwks = { type = "object"; description = "JSON Web Key Set, RFC 7517" };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   935
		software_id = { type = "string"; format = "uuid" };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   936
		software_version = { type = "string" };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   937
	};
5369
698fef74ce53 mod_http_oauth2: Allow only l10n variants of name in client metadata
Kim Alvefur <zash@zash.se>
parents: 5368
diff changeset
   938
	luaPatternProperties = {
698fef74ce53 mod_http_oauth2: Allow only l10n variants of name in client metadata
Kim Alvefur <zash@zash.se>
parents: 5368
diff changeset
   939
		-- Localized versions of descriptive properties and URIs
698fef74ce53 mod_http_oauth2: Allow only l10n variants of name in client metadata
Kim Alvefur <zash@zash.se>
parents: 5368
diff changeset
   940
		["^client_name#"] = { description = "Localized version of 'client_name'"; type = "string" };
698fef74ce53 mod_http_oauth2: Allow only l10n variants of name in client metadata
Kim Alvefur <zash@zash.se>
parents: 5368
diff changeset
   941
		["^[a-z_]+_uri#"] = { type = "string"; format = "uri"; luaPattern = "^https:" };
698fef74ce53 mod_http_oauth2: Allow only l10n variants of name in client metadata
Kim Alvefur <zash@zash.se>
parents: 5368
diff changeset
   942
	};
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   943
}
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   944
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
   945
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
   946
	local uri = url.parse(redirect_uri);
5461
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
   947
	if not uri.scheme then
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
   948
		return false; -- no relative URLs
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
   949
	end
5371
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
   950
	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
   951
		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
   952
	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
   953
		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
   954
	end
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
   955
end
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5370
diff changeset
   956
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
   957
function create_client(client_metadata)
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   958
	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
   959
		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
   960
	end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
   961
5370
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
   962
	-- Fill in default values
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
   963
	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
   964
		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
   965
			client_metadata[propname] = propspec.default;
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
   966
		end
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
   967
	end
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5369
diff changeset
   968
5250
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   969
	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
   970
	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
   971
		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
   972
	end
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5249
diff changeset
   973
5243
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   974
	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
   975
		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
   976
			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
   977
		end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   978
	end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
   979
5248
fa7bd721a3f6 mod_http_oauth2: Fix validation of informative URIs
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
   980
	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
   981
		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
   982
			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
   983
				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
   984
			end
5243
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   985
		end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   986
	end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
   987
5361
eda3b078ba2c mod_http_oauth2: Validate (unused at this point) localized URIs
Kim Alvefur <zash@zash.se>
parents: 5360
diff changeset
   988
	for k, v in pairs(client_metadata) do
5408
1087f697c3f3 mod_http_oauth2: Strip unknown extra fields from client registration
Kim Alvefur <zash@zash.se>
parents: 5407
diff changeset
   989
		local base_k = k:match"^([^#]+)#" or k;
1087f697c3f3 mod_http_oauth2: Strip unknown extra fields from client registration
Kim Alvefur <zash@zash.se>
parents: 5407
diff changeset
   990
		if not registration_schema.properties[base_k] or k:find"^client_uri#" then
1087f697c3f3 mod_http_oauth2: Strip unknown extra fields from client registration
Kim Alvefur <zash@zash.se>
parents: 5407
diff changeset
   991
			-- Ignore and strip unknown extra properties
1087f697c3f3 mod_http_oauth2: Strip unknown extra fields from client registration
Kim Alvefur <zash@zash.se>
parents: 5407
diff changeset
   992
			client_metadata[k] = nil;
1087f697c3f3 mod_http_oauth2: Strip unknown extra fields from client registration
Kim Alvefur <zash@zash.se>
parents: 5407
diff changeset
   993
		elseif k:find"_uri#" then
1087f697c3f3 mod_http_oauth2: Strip unknown extra fields from client registration
Kim Alvefur <zash@zash.se>
parents: 5407
diff changeset
   994
			-- Localized URIs should be secure too
5407
c574aaaa4d57 mod_http_oauth2: Simplify validation of various URIs
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
   995
			if not redirect_uri_allowed(v, client_uri, "web") then
c574aaaa4d57 mod_http_oauth2: Simplify validation of various URIs
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
   996
				return nil, oauth_error("invalid_client_metadata", "Invalid, insecure or inappropriate informative URI");
5361
eda3b078ba2c mod_http_oauth2: Validate (unused at this point) localized URIs
Kim Alvefur <zash@zash.se>
parents: 5360
diff changeset
   997
			end
eda3b078ba2c mod_http_oauth2: Validate (unused at this point) localized URIs
Kim Alvefur <zash@zash.se>
parents: 5360
diff changeset
   998
		end
eda3b078ba2c mod_http_oauth2: Validate (unused at this point) localized URIs
Kim Alvefur <zash@zash.se>
parents: 5360
diff changeset
   999
	end
eda3b078ba2c mod_http_oauth2: Validate (unused at this point) localized URIs
Kim Alvefur <zash@zash.se>
parents: 5360
diff changeset
  1000
5410
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1001
	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
  1002
	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
  1003
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1004
	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
  1005
		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
  1006
	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
  1007
		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
  1008
	end
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1009
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1010
	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
  1011
		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
  1012
	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
  1013
		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
  1014
	end
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
  1015
5247
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
  1016
	-- Ensure each signed client_id JWT is unique, short ID and issued at
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
  1017
	-- timestamp should be sufficient to rule out brute force attacks
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5246
diff changeset
  1018
	client_metadata.nonce = id.short();
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1019
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1020
	-- 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
  1021
	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
  1022
5225
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
  1023
	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
  1024
	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
  1025
5411
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1026
	if client_metadata.token_endpoint_auth_method ~= "none" then
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1027
		local client_secret = make_client_secret(client_id);
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1028
		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
  1029
		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
  1030
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5410
diff changeset
  1031
		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
  1032
			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
  1033
		end
5206
b81fd0d22c66 mod_http_oauth2: Calculate client secret expiry in registration response
Kim Alvefur <zash@zash.se>
parents: 5205
diff changeset
  1034
	end
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1035
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1036
	return client_metadata;
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1037
end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1038
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1039
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
  1040
	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
  1041
	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
  1042
	if err then
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1043
		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
  1044
	end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1045
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1046
	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
  1047
	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
  1048
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1049
	return {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1050
		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
  1051
		headers = {
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5506
diff changeset
  1052
			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
  1053
			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
  1054
			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
  1055
		};
5263
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5262
diff changeset
  1056
		body = json.encode(response);
5197
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1057
	};
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1058
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1059
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1060
if not registration_key then
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1061
	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
  1062
	handle_authorization_request = nil
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1063
	handle_register_request = nil
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1064
end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
  1065
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1066
local function handle_userinfo_request(event)
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1067
	local request = event.request;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1068
	local credentials = get_request_credentials(request);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1069
	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
  1070
		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
  1071
		return 401;
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1072
	end
5340
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5339
diff changeset
  1073
	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
  1074
	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
  1075
		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
  1076
		return 403;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1077
	end
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1078
	local scopes = set.new()
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1079
	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
  1080
		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
  1081
	else
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1082
		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
  1083
	end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1084
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1085
	if not scopes:contains("openid") then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1086
		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
  1087
		-- 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
  1088
		return 403;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1089
	end
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1090
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1091
	local user_info = {
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1092
		iss = get_issuer();
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1093
		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
  1094
	}
5341
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1095
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1096
	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
  1097
	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
  1098
	if not token_claims:empty() then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1099
		-- Another module can do that
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1100
		module:fire_event("token/userinfo", {
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1101
			token = token_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1102
			claims = token_claims;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1103
			username = jid.split(token_info.jid);
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1104
			userinfo = user_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1105
		});
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1106
	end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5340
diff changeset
  1107
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1108
	return {
5262
9629971e307f mod_http_oauth2: Fix userinfo status code off-by-one
Kim Alvefur <zash@zash.se>
parents: 5261
diff changeset
  1109
		status_code = 200;
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1110
		headers = { content_type = "application/json" };
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1111
		body = json.encode(user_info);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1112
	};
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1113
end
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1114
3907
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1115
module:depends("http");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1116
module:provides("http", {
5484
5108f63e762b mod_http_oauth2: Allow CORS for browser clients
Kim Alvefur <zash@zash.se>
parents: 5483
diff changeset
  1117
	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
  1118
	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
  1119
		-- 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
  1120
		-- 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
  1121
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1122
		-- 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
  1123
		["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
  1124
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1125
		-- 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
  1126
		["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
  1127
		["POST /authorize"] = handle_authorization_request;
5552
fd3c12c40cd9 mod_http_oauth2: Disable CORS for authorization endpoint
Kim Alvefur <zash@zash.se>
parents: 5551
diff changeset
  1128
		["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
  1129
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1130
		-- 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
  1131
		-- 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
  1132
		-- is delivered here.
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1133
5386
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1134
		-- 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
  1135
		["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
  1136
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1137
		-- 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
  1138
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
  1139
		-- 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
  1140
		["POST /revoke"] = handle_revocation_request;
5249
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1141
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5248
diff changeset
  1142
		-- OpenID
5232
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
  1143
		["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
  1144
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1145
		-- Optional static content for templates
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1146
		["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
  1147
			headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1148
				["Content-Type"] = "text/css";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1149
			};
5548
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5530
diff changeset
  1150
			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
  1151
		} or nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1152
		["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
  1153
			headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1154
				["Content-Type"] = "text/javascript";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1155
			};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1156
			body = templates.js;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5211
diff changeset
  1157
		} or nil;
5397
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5396
diff changeset
  1158
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5396
diff changeset
  1159
		-- Some convenient fallback handlers
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5396
diff changeset
  1160
		["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
  1161
		["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
  1162
		["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
  1163
	};
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1164
});
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1165
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1166
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
  1167
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1168
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
  1169
	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
  1170
	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
  1171
		return;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1172
	end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
  1173
	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
  1174
	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
  1175
	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
  1176
end, 5);
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1177
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1178
-- OIDC Discovery
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1179
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1180
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
  1181
	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
  1182
		return authorization_server_metadata;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1183
	end
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1184
	authorization_server_metadata = {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1185
		-- 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
  1186
		issuer = get_issuer();
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1187
		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
  1188
		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
  1189
		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
  1190
		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
  1191
			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
  1192
		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
  1193
		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
  1194
		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
  1195
		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
  1196
		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
  1197
		revocation_endpoint_auth_methods_supported = array({ "client_secret_basic" });
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1198
		code_challenge_methods_supported = array(it.keys(verifier_transforms));
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1199
		grant_types_supported = array(it.keys(response_type_handlers)):map(tmap {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1200
			token = "implicit";
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1201
			code = "authorization_code";
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1202
		});
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1203
		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
  1204
		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
  1205
		service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html");
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1206
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1207
		-- OpenID
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1208
		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
  1209
		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
  1210
		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
  1211
	}
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1212
	return authorization_server_metadata;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1213
end
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1214
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1215
module:provides("http", {
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1216
	name = "oauth2-discovery";
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1217
	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
  1218
	cors = { enabled = true };
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1219
	route = {
5506
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1220
		["GET"] = function()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1221
			return {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1222
				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
  1223
				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
  1224
			}
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5505
diff changeset
  1225
		end
5193
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1226
	};
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1227
});
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1228
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5192
diff changeset
  1229
module:shared("tokenauth/oauthbearer_config").oidc_discovery_url = module:http_url("oauth2-discovery", "/.well-known/oauth-authorization-server");