annotate mod_http_oauth2/mod_http_oauth2.lua @ 6265:30adcea825c3

mod_conversejs: Fix hostname set as default username (thanks roughnecks) In login mode, it seems jid is used as default value in the login field but it was only needed in anonymous mode.
author Kim Alvefur <zash@zash.se>
date Wed, 18 Jun 2025 14:28:38 +0200
parents e1c54de06905
children 8108aec64fb9
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
5501
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
1 local usermanager = require "core.usermanager";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
2 local url = require "socket.url";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
3 local array = require "util.array";
4271
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
4 local cache = require "util.cache";
5501
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
5 local encodings = require "util.encodings";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
6 local errors = require "util.error";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
7 local hashes = require "util.hashes";
3903
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";
5501
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
9 local id = require "util.id";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
10 local it = require "util.iterators";
3903
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";
5501
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
13 local schema = require "util.jsonschema";
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
14 local jwt = require "util.jwt";
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
15 local random = require "util.random";
5209
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5208
diff changeset
16 local set = require "util.set";
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
17 local st = require "util.stanza";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
18
5501
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
19 local base64 = encodings.base64;
57ce8c4017e7 mod_http_oauth2: Sort imports
Kim Alvefur <zash@zash.se>
parents: 5495
diff changeset
20
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
21 local function b64url(s)
5392
c0a6f39caf47 mod_http_oauth2: Fix missing base64 part of base64url (Thanks KeyCloak)
Kim Alvefur <zash@zash.se>
parents: 5391
diff changeset
22 return (base64.encode(s):gsub("[+/=]", { ["+"] = "-", ["/"] = "_", ["="] = "" }))
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
23 end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
24
5400
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5399
diff changeset
25 local function tmap(t)
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5399
diff changeset
26 return function(k)
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5399
diff changeset
27 return t[k];
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5399
diff changeset
28 end
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5399
diff changeset
29 end
71766a4a7322 mod_http_oauth2: Reduce line count of metadata construction
Kim Alvefur <zash@zash.se>
parents: 5399
diff changeset
30
5956
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
31 local function strict_url_parse(urlstr)
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
32 local url_parts = url.parse(urlstr);
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
33 if not url_parts then return url_parts; end
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
34 if url_parts.userinfo then return false; end
5957
e8bf46a7bb27 mod_http_oauth2: Ensure URL ports are integer in correct range
Kim Alvefur <zash@zash.se>
parents: 5956
diff changeset
35 if url_parts.port then
e8bf46a7bb27 mod_http_oauth2: Ensure URL ports are integer in correct range
Kim Alvefur <zash@zash.se>
parents: 5956
diff changeset
36 local port = tonumber(url_parts.port);
e8bf46a7bb27 mod_http_oauth2: Ensure URL ports are integer in correct range
Kim Alvefur <zash@zash.se>
parents: 5956
diff changeset
37 if not port then return false; end
5960
538f468f9a65 mod_http_oauth2: Simplify negation in condition
Kim Alvefur <zash@zash.se>
parents: 5958
diff changeset
38 if port <= 0 or port > 0xffff then return false; end
5957
e8bf46a7bb27 mod_http_oauth2: Ensure URL ports are integer in correct range
Kim Alvefur <zash@zash.se>
parents: 5956
diff changeset
39 if port ~= math.floor(port) then return false; end
e8bf46a7bb27 mod_http_oauth2: Ensure URL ports are integer in correct range
Kim Alvefur <zash@zash.se>
parents: 5956
diff changeset
40 end
5958
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
41 if url_parts.host then
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
42 if encodings.stringprep.nameprep(url_parts.host) ~= url_parts.host then
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
43 return false;
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
44 end
5961
7308ec4aaad1 mod_http_oauth2: Fix error due to mistake in 5f8a306c8306
Kim Alvefur <zash@zash.se>
parents: 5960
diff changeset
45 if not encodings.idna.to_ascii(url_parts.host) then
5958
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
46 return false;
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
47 end
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
48 end
5956
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
49 return url_parts;
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
50 end
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
51
5513
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
52 local function strict_formdecode(query)
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
53 if not query then
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
54 return nil;
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
55 end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
56 local params = http.formdecode(query);
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
57 if type(params) ~= "table" then
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
58 return nil, "no-pairs";
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
59 end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
60 local dups = {};
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
61 for _, pair in ipairs(params) do
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
62 if dups[pair.name] then
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
63 return nil, "duplicate";
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
64 end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
65 dups[pair.name] = true;
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
66 end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
67 return params;
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
68 end
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
69
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
70 local function read_file(base_path, fn, required)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
71 local f, err = io.open(base_path .. "/" .. fn);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
72 if not f then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
73 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: 5207
diff changeset
74 if required then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
75 return error("Failed to load templates");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
76 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
77 return nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
78 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
79 local data = assert(f:read("*a"));
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
80 assert(f:close());
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
81 return data;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
82 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
83
5554
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
84 local allowed_locales = module:get_option_array("allowed_oauth2_locales", {});
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
85 -- TODO Allow translations or per-locale templates somehow.
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
86
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
87 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: 5207
diff changeset
88 local templates = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
89 login = read_file(template_path, "login.html", true);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
90 consent = read_file(template_path, "consent.html", true);
5495
7998b49d6512 mod_http_oauth2: Create proper template for OOB code delivery
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
91 oob = read_file(template_path, "oob.html", true);
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
92 device = read_file(template_path, "device.html", true);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
93 error = read_file(template_path, "error.html", true);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
94 css = read_file(template_path, "style.css");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
95 js = read_file(template_path, "script.js");
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
96 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
97
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
98 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: 5207
diff changeset
99
5547
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5544
diff changeset
100 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: 5544
diff changeset
101
5544
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5526
diff changeset
102 local render_html = require"util.interpolation".new("%b{}", st.xml_escape);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
103 local function render_page(template, data, sensitive)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
104 data = data or {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
105 data.site_name = site_name;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
106 local resp = {
5470
40c990159006 mod_http_oauth2: Use error status code when rendering error page
Kim Alvefur <zash@zash.se>
parents: 5469
diff changeset
107 status_code = data.error and data.error.code or 200;
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
108 headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
109 ["Content-Type"] = "text/html; charset=utf-8";
5547
d4a2997deae9 mod_http_oauth2: Make CSP configurable
Kim Alvefur <zash@zash.se>
parents: 5544
diff changeset
110 ["Content-Security-Policy"] = security_policy;
5479
30e2722c9fa3 mod_http_oauth2: Disable Referrer via header
Kim Alvefur <zash@zash.se>
parents: 5478
diff changeset
111 ["Referrer-Policy"] = "no-referrer";
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
112 ["X-Frame-Options"] = "DENY";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
113 ["Cache-Control"] = (sensitive and "no-store" or "no-cache")..", private";
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
114 ["Pragma"] = "no-cache";
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
115 };
5544
cb141088eff0 mod_http_oauth2: Remove underscore prefix
Kim Alvefur <zash@zash.se>
parents: 5526
diff changeset
116 body = render_html(template, data);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
117 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
118 return resp;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
119 end
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
120
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
121 local authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
122
3915
80dffbbd056b mod_rest, mod_http_oauth2: Switch from mod_authtokens to mod_tokenauth per Prosody bf81523e2ff4
Matthew Wild <mwild1@gmail.com>
parents: 3908
diff changeset
123 local tokens = module:depends("tokenauth");
3908
8ac5d9933106 mod_http_oauth2: Implement real tokens using mod_authtokens
Matthew Wild <mwild1@gmail.com>
parents: 3903
diff changeset
124
5617
d8622797e315 mod_http_oauth2: Shorten default token validity periods
Kim Alvefur <zash@zash.se>
parents: 5616
diff changeset
125 local default_access_ttl = module:get_option_number("oauth2_access_token_ttl", 3600);
5619
81042c2a235a mod_http_oauth2: Don't use new time period API just yet
Kim Alvefur <zash@zash.se>
parents: 5618
diff changeset
126 local default_refresh_ttl = module:get_option_number("oauth2_refresh_token_ttl", 604800);
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
127
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
128 -- 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: 5192
diff changeset
129 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: 5192
diff changeset
130 local registration_algo = module:get_option_string("oauth2_registration_algorithm", "HS256");
5416
2393dbae51ed mod_http_oauth2: Add option for specifying TTL of registered clients
Kim Alvefur <zash@zash.se>
parents: 5409
diff changeset
131 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: 5409
diff changeset
132 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: 5409
diff changeset
133 { default_ttl = registration_ttl; accept_expired = not registration_ttl });
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
134
5716
426c42c11f89 mod_http_oauth2: Make defaults more secure
Kim Alvefur <zash@zash.se>
parents: 5715
diff changeset
135 local pkce_required = module:get_option_boolean("oauth2_require_code_challenge", true);
426c42c11f89 mod_http_oauth2: Make defaults more secure
Kim Alvefur <zash@zash.se>
parents: 5715
diff changeset
136 local respect_prompt = module:get_option_boolean("oauth2_respect_oidc_prompt", false);
6207
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
137 local expect_username_jid = module:get_option_boolean("oauth2_expect_username_jid", false);
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
138
5199
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5198
diff changeset
139 local verification_key;
5459
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5458
diff changeset
140 local sign_client, verify_client;
5196
6b63af56c8ac mod_http_oauth2: Remove error message
Kim Alvefur <zash@zash.se>
parents: 5195
diff changeset
141 if registration_key then
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
142 -- Tie it to the host if global
5199
f48628dc83f1 mod_http_oauth2: Separate client_secret verification key from JWT key
Kim Alvefur <zash@zash.se>
parents: 5198
diff changeset
143 verification_key = hashes.hmac_sha256(registration_key, module.host);
5459
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5458
diff changeset
144 sign_client, verify_client = jwt.init(registration_algo, registration_key, registration_key, registration_options);
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
145 end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
146
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
147 local new_device_token, verify_device_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
148
5510
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
149 -- verify and prepare client structure
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
150 local function check_client(client_id)
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
151 if not verify_client then
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
152 return nil, "client-registration-not-enabled";
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
153 end
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
154
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
155 local ok, client = verify_client(client_id);
5511
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
156 if not ok then
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
157 return ok, client;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
158 end
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
159
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
160 client.client_hash = b64url(hashes.sha256(client_id));
5510
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
161 return client;
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
162 end
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
163
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
164 local purpose_map = { ["oauth2-refresh"] = "refresh_token"; ["oauth"] = "access_token" };
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
165
5449
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
166 -- scope : string | array | set
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
167 --
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
168 -- 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: 5448
diff changeset
169 -- (all ( client ( grant ( token ) ) ))
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
170 -- 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: 5448
diff changeset
171
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
172 -- string -> array
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
173 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: 5252
diff changeset
174 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: 5252
diff changeset
175 end
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
176
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
177 local openid_claims = set.new();
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
178 module:add_item("openid-claim", "openid");
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
179
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
180 module:handle_items("openid-claim", function(event)
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
181 authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
182 openid_claims:add(event.item);
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
183 end, function()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
184 authorization_server_metadata = nil;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
185 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: 5501
diff changeset
186 end, true);
5337
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
187
5449
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
188 -- array -> array, array, array
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
189 local function split_scopes(scope_list)
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
190 local claims, roles, unknown = array(), array(), array();
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
191 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: 5416
diff changeset
192 for _, scope in ipairs(scope_list) do
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
193 if openid_claims:contains(scope) then
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
194 claims:push(scope);
5467
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5466
diff changeset
195 elseif scope == "xmpp" or all_roles[scope] then
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
196 roles:push(scope);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
197 else
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
198 unknown:push(scope);
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
199 end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
200 end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
201 return claims, roles, unknown;
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
202 end
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
203
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
204 local function can_assume_role(username, requested_role)
5467
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5466
diff changeset
205 return requested_role == "xmpp" or usermanager.user_can_assume_role(username, module.host, requested_role);
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
206 end
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
207
5449
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
208 -- function (string) : function(string) : boolean
5427
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
209 local function role_assumable_by(username)
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
210 return function(role)
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
211 return can_assume_role(username, role);
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
212 end
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
213 end
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
214
5449
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
215 -- string, array --> array
5426
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5425
diff changeset
216 local function user_assumable_roles(username, requested_roles)
5427
d69c10327d6d mod_http_oauth2: More functional functions
Kim Alvefur <zash@zash.se>
parents: 5426
diff changeset
217 return array.filter(requested_roles, role_assumable_by(username));
5426
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5425
diff changeset
218 end
f75d95f27da7 mod_http_oauth2: Add function for filtering roles
Kim Alvefur <zash@zash.se>
parents: 5425
diff changeset
219
5449
9c19a6b8e542 mod_http_oauth2: Describe type signatures of scope handling functions
Kim Alvefur <zash@zash.se>
parents: 5448
diff changeset
220 -- string, string|nil --> string, string
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
221 local function filter_scopes(username, requested_scope_string)
5428
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5427
diff changeset
222 local requested_scopes, requested_roles = split_scopes(parse_scopes(requested_scope_string or ""));
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
223
5428
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5427
diff changeset
224 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: 5427
diff changeset
225 local granted_scopes = requested_scopes + granted_roles;
5417
3902082c42c4 mod_http_oauth2: Refactor scope handling into smaller functions
Kim Alvefur <zash@zash.se>
parents: 5416
diff changeset
226
5428
07e166b34c4c mod_http_oauth2: Simplify code with the power of first class functions
Kim Alvefur <zash@zash.se>
parents: 5427
diff changeset
227 local selected_role = granted_roles[1];
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
228
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
229 return granted_scopes:concat(" "), selected_role;
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
230 end
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
231
5213
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5210
diff changeset
232 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: 5210
diff changeset
233 return os.difftime(code.expires, os.time());
4669
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
234 end
d3434fd151b5 mod_http_oauth2: Optimize cleanup timer
Kim Alvefur <zash@zash.se>
parents: 4370
diff changeset
235
5213
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5210
diff changeset
236 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: 5210
diff changeset
237 return code_expires_in(code) < 0;
4269
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
238 end
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
239
5751
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
240 -- LRU cache for short-term storage of authorization codes and device codes
4271
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
241 local codes = cache.new(10000, function (_, code)
5751
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
242 -- If the cache is full and the oldest item hasn't expired yet then we
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
243 -- might be under some kind of DoS attack, so might as well reject further
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
244 -- entries for a bit.
4271
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
245 return code_expired(code)
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
246 end);
9623b99bb8d2 mod_http_oauth2: Keep authorization codes in memory instead of storage
Kim Alvefur <zash@zash.se>
parents: 4270
diff changeset
247
5618
869c01d91aea mod_http_oauth2: Clean cache less frequently
Kim Alvefur <zash@zash.se>
parents: 5617
diff changeset
248 -- Clear out unredeemed codes so they don't linger in memory.
869c01d91aea mod_http_oauth2: Clean cache less frequently
Kim Alvefur <zash@zash.se>
parents: 5617
diff changeset
249 module:daily("Clear expired authorization codes", function()
5751
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
250 -- The tail should be the least recently touched item, and most likely to
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
251 -- have expired already, so check and remove that one until encountering
d563a6b0dfb7 mod_http_oauth2: Comment on authorization code storage
Kim Alvefur <zash@zash.se>
parents: 5716
diff changeset
252 -- one that has not expired.
4272
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
253 local k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
254 while code and code_expired(code) do
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
255 codes:set(k, nil);
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
256 k, code = codes:tail();
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
257 end
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
258 end)
91b951fb3018 mod_http_oauth2: Periodically trim unused authorization codes
Kim Alvefur <zash@zash.se>
parents: 4271
diff changeset
259
5207
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5206
diff changeset
260 local function get_issuer()
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5206
diff changeset
261 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: 5206
diff changeset
262 end
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5206
diff changeset
263
5458
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5457
diff changeset
264 -- 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: 5457
diff changeset
265 -- 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: 5457
diff changeset
266 -- 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: 5457
diff changeset
267 local oob_uri = "urn:ietf:wg:oauth:2.0:oob";
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
268 local device_uri = "urn:ietf:params:oauth:grant-type:device_code";
5458
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5457
diff changeset
269
5209
942f8a2f722d mod_http_oauth2: Allow non-HTTPS on localhost URLs
Matthew Wild <mwild1@gmail.com>
parents: 5208
diff changeset
270 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: 5208
diff changeset
271
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
272 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
273 return errors.new({
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
274 type = "modify";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
275 condition = "bad-request";
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
276 code = err_name == "invalid_client" and 401 or 400;
5798
fdf3056021dc mod_http_oauth2: Tweak fallback error text
Kim Alvefur <zash@zash.se>
parents: 5797
diff changeset
277 text = err_desc or err_name:gsub("^.", string.upper):gsub("_", " ");
4276
ec33b3b1136c mod_http_oauth2: Fix passing OAuth-specific error details
Kim Alvefur <zash@zash.se>
parents: 4272
diff changeset
278 extra = { oauth2_response = { error = err_name, error_description = err_desc } };
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
279 });
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
280 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
281
5248
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
282 -- 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: 5247
diff changeset
283 -- 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: 5247
diff changeset
284 -- 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: 5247
diff changeset
285 local function client_subset(client)
5511
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
286 return {
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
287 name = client.client_name;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
288 uri = client.client_uri;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
289 id = client.software_id;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
290 version = client.software_version;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
291 hash = client.client_hash;
0860497152af mod_http_oauth2: Record hash of client_id to allow future verification
Kim Alvefur <zash@zash.se>
parents: 5510
diff changeset
292 };
5248
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
293 end
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
294
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
295 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: 5278
diff changeset
296 local token_data = { oauth2_scopes = scope_string, oauth2_client = nil };
5248
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
297 if client then
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
298 token_data.oauth2_client = client_subset(client);
5248
b8b2bf0c1b4b mod_http_oauth2: Record details of OAuth client a token is issued to
Kim Alvefur <zash@zash.se>
parents: 5247
diff changeset
299 end
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
300 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: 5252
diff changeset
301 token_data = nil;
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
302 end
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
303
5280
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5279
diff changeset
304 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: 5279
diff changeset
305 if not grant then
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5279
diff changeset
306 -- No existing grant, create one
5646
d67980d9e12d mod_http_oauth2: Apply refresh token ttl to refresh token instead of grant
Kim Alvefur <zash@zash.se>
parents: 5643
diff changeset
307 grant = tokens.create_grant(token_jid, token_jid, nil, token_data);
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
308 end
5280
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5279
diff changeset
309
5616
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
310 if refresh_token_info then
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
311 -- out with the old refresh tokens
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
312 local ok, err = tokens.revoke_token(refresh_token_info.token);
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
313 if not ok then
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
314 module:log("error", "Could not revoke refresh token: %s", err);
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
315 return 500;
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
316 end
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
317 end
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
318 -- in with the new refresh token
5646
d67980d9e12d mod_http_oauth2: Apply refresh token ttl to refresh token instead of grant
Kim Alvefur <zash@zash.se>
parents: 5643
diff changeset
319 local refresh_token = refresh_token_info ~= false and tokens.create_token(token_jid, grant.id, nil, default_refresh_ttl, "oauth2-refresh");
5616
59d5fc50f602 mod_http_oauth2: Implement refresh token rotation
Kim Alvefur <zash@zash.se>
parents: 5614
diff changeset
320
5467
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5466
diff changeset
321 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: 5466
diff changeset
322 -- 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: 5466
diff changeset
323 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: 5466
diff changeset
324 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: 5466
diff changeset
325 end
1c78a97a1091 mod_http_oauth2: Add a special "xmpp" scope that grants the users' default role
Kim Alvefur <zash@zash.se>
parents: 5466
diff changeset
326
5451
6705f2a09702 mod_http_oauth2: Reference grant by id instead of value
Kim Alvefur <zash@zash.se>
parents: 5450
diff changeset
327 local access_token, access_token_info = tokens.create_token(token_jid, grant.id, role, default_access_ttl, "oauth2");
5280
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5279
diff changeset
328
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
329 local expires_at = access_token_info.expires;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
330 return {
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
331 token_type = "bearer";
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
332 access_token = access_token;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
333 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: 5278
diff changeset
334 scope = scope_string;
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
335 id_token = id_token;
5280
eb482defd9b0 mod_http_oauth2: Update to use new API of Prosody mod_tokenauth @ 601d9a375b86
Matthew Wild <mwild1@gmail.com>
parents: 5279
diff changeset
336 refresh_token = refresh_token or nil;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
337 };
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
338 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
339
5461
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
340 local function normalize_loopback(uri)
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
341 local u = url.parse(uri);
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
342 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: 5460
diff changeset
343 u.authority = nil;
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
344 u.host = "::1";
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
345 u.port = nil;
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
346 return url.build(u);
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
347 end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
348 -- 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: 5460
diff changeset
349 end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
350
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
351 local function get_redirect_uri(client, query_redirect_uri) -- record client, string : string
5219
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
352 if not query_redirect_uri then
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
353 if #client.redirect_uris ~= 1 then
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
354 -- 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: 5218
diff changeset
355 return;
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
356 end
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
357 -- 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: 5218
diff changeset
358 return client.redirect_uris[1];
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
359 end
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
360 if query_redirect_uri == device_uri and client.grant_types then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
361 for _, grant_type in ipairs(client.grant_types) do
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
362 if grant_type == device_uri then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
363 return query_redirect_uri;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
364 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
365 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
366 -- Tried to use device authorization flow without registering it.
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
367 return;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
368 end
5219
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
369 -- Verify the client-provided URI matches one previously registered
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
370 for _, redirect_uri in ipairs(client.redirect_uris) do
5219
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
371 if query_redirect_uri == redirect_uri then
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
372 return redirect_uri
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
373 end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
374 end
5461
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
375 -- 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: 5460
diff changeset
376 -- 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: 5460
diff changeset
377 -- 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: 5460
diff changeset
378 -- of the request.
5460
c0d62c1b4424 mod_http_oauth2: Add FIXME about loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5459
diff changeset
379 -- https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-08.html#section-8.4.2
5461
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
380 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: 5460
diff changeset
381 if loopback_redirect_uri then
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
382 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: 5460
diff changeset
383 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: 5460
diff changeset
384 return query_redirect_uri;
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
385 end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
386 end
06640647d193 mod_http_oauth2: Fix use of arbitrary ports in loopback redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5460
diff changeset
387 end
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
388 end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
389
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
390 local grant_type_handlers = {};
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
391 local response_type_handlers = {};
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
392 local verifier_transforms = {};
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
393
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
394 function grant_type_handlers.implicit()
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
395 -- Placeholder to make discovery work correctly.
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
396 -- Access tokens are delivered via redirect when using the implict flow, not
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
397 -- via the token endpoint, so how did you get here?
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
398 return oauth_error("invalid_request");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
399 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
400
6204
4f0ed0e3ad5a mod_http_oauth2: Require client authentication for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6179
diff changeset
401 local function make_client_secret(client_id) --> client_secret
6233
9014c95c4549 mod_http_oauth2: Fix indentation
Kim Alvefur <zash@zash.se>
parents: 6232
diff changeset
402 return hashes.hmac_sha256(verification_key, client_id, true);
6204
4f0ed0e3ad5a mod_http_oauth2: Require client authentication for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6179
diff changeset
403 end
4f0ed0e3ad5a mod_http_oauth2: Require client authentication for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6179
diff changeset
404
4f0ed0e3ad5a mod_http_oauth2: Require client authentication for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6179
diff changeset
405 local function verify_client_secret(client_id, client_secret)
6233
9014c95c4549 mod_http_oauth2: Fix indentation
Kim Alvefur <zash@zash.se>
parents: 6232
diff changeset
406 return hashes.equals(make_client_secret(client_id), client_secret);
6204
4f0ed0e3ad5a mod_http_oauth2: Require client authentication for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6179
diff changeset
407 end
4f0ed0e3ad5a mod_http_oauth2: Require client authentication for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6179
diff changeset
408
6232
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
409 function grant_type_handlers.password(params, client)
6207
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
410 local request_username
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
411
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
412 if expect_username_jid then
6244
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
413 local request_jid = params.username;
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
414 if not request_jid then
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
415 return oauth_error("invalid_request", "missing 'username' (JID)");
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
416 end
6234
b63202d66238 mod_http_oauth2: Remove unused variable [luacheck]
Kim Alvefur <zash@zash.se>
parents: 6233
diff changeset
417 local _request_username, request_host = jid.prepped_split(request_jid);
6207
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
418
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
419 if not (_request_username and request_host) or request_host ~= module.host then
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
420 return oauth_error("invalid_request", "invalid JID");
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
421 end
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
422
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
423 request_username = _request_username
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
424 else
6244
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
425 request_username = params.username;
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
426 if not request_username then
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
427 return oauth_error("invalid_request", "missing 'username'");
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
428 end
6207
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
429 end
ab14e7ecb82f mod_http_oauth2: Allow JIDs as username for password grant
magicfelix <felix@felix-zauberer.de>
parents: 6206
diff changeset
430
6244
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
431 local request_password = params.password;
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
432 if not request_password then
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
433 return oauth_error("invalid_request", "missing 'password'");
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
434 end
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
435
6245
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
436 local auth_event = {
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
437 session = {
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
438 type = "oauth2";
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
439 ip = "::";
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
440 username = request_username;
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
441 host = module.host;
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
442 log = module._log;
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
443 sasl_handler = { username = request_username; selected = "x-oauth2-password" };
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
444 client_id = client.client_name;
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
445 };
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
446 };
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
447
6205
c1b94dd6e53b mod_http_oauth2: Change password grant to take username instead of JID [BC]
Kim Alvefur <zash@zash.se>
parents: 6204
diff changeset
448 if not usermanager.test_password(request_username, module.host, request_password) then
6245
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
449 module:fire_event("authentication-failure", auth_event);
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
450 return oauth_error("invalid_grant", "incorrect credentials");
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
451 end
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
452
6245
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
453 module:fire_event("authentication-success", auth_event);
7e4238d2989c mod_http_oauth2: Fire authentication events in password grant
Kim Alvefur <zash@zash.se>
parents: 6244
diff changeset
454
6205
c1b94dd6e53b mod_http_oauth2: Change password grant to take username instead of JID [BC]
Kim Alvefur <zash@zash.se>
parents: 6204
diff changeset
455 local granted_jid = jid.join(request_username, module.host);
5256
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5255
diff changeset
456 local granted_scopes, granted_role = filter_scopes(request_username, params.scope);
6206
a931a95e363e mod_http_oauth2: Pass client to token to enable introspection
magicfelix <felix@felix-zauberer.de>
parents: 6205
diff changeset
457 return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, client));
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
458 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
459
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
460 function response_type_handlers.code(client, params, granted_jid, id_token)
5191
f5a58cbe86e4 mod_http_oauth2: Derive scope from correct user details
Kim Alvefur <zash@zash.se>
parents: 5190
diff changeset
461 local request_username, request_host = jid.split(granted_jid);
5256
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5255
diff changeset
462 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: 5255
diff changeset
463 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: 5255
diff changeset
464 end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5255
diff changeset
465 local granted_scopes, granted_role = filter_scopes(request_username, params.scope);
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
466
5796
93d6e9026c1b mod_http_oauth2: Do not enforce PKCE on Device and OOB flows
Kim Alvefur <zash@zash.se>
parents: 5771
diff changeset
467 local redirect_uri = get_redirect_uri(client, params.redirect_uri);
93d6e9026c1b mod_http_oauth2: Do not enforce PKCE on Device and OOB flows
Kim Alvefur <zash@zash.se>
parents: 5771
diff changeset
468
93d6e9026c1b mod_http_oauth2: Do not enforce PKCE on Device and OOB flows
Kim Alvefur <zash@zash.se>
parents: 5771
diff changeset
469 if pkce_required and not params.code_challenge and redirect_uri ~= device_uri and redirect_uri ~= oob_uri then
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
470 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: 5382
diff changeset
471 end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
472
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
473 local prefix = "authorization_code:";
5243
d5dc8edb2695 mod_http_oauth2: Use more compact IDs
Kim Alvefur <zash@zash.se>
parents: 5242
diff changeset
474 local code = id.medium();
5796
93d6e9026c1b mod_http_oauth2: Do not enforce PKCE on Device and OOB flows
Kim Alvefur <zash@zash.se>
parents: 5771
diff changeset
475 if redirect_uri == device_uri then
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
476 local is_device, device_state = verify_device_token(params.state);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
477 if is_device then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
478 -- reconstruct the device_code
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
479 prefix = "device_code:";
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
480 code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
5628
9aace51c3637 mod_http_oauth2: Bail on invalid or expired device flow state token
Kim Alvefur <zash@zash.se>
parents: 5626
diff changeset
481 else
9aace51c3637 mod_http_oauth2: Bail on invalid or expired device flow state token
Kim Alvefur <zash@zash.se>
parents: 5626
diff changeset
482 return oauth_error("invalid_request");
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
483 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
484 end
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
485 local ok = codes:set(prefix.. params.client_id .. "#" .. code, {
5213
dc0f502c12f1 mod_http_oauth2: Fix authorization code logic
Kim Alvefur <zash@zash.se>
parents: 5210
diff changeset
486 expires = os.time() + 600;
4340
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
487 granted_jid = granted_jid;
7cd3b7ec59e9 mod_http_oauth2: Rudimentary support for scopes (but not really)
Matthew Wild <mwild1@gmail.com>
parents: 4276
diff changeset
488 granted_scopes = granted_scopes;
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
489 granted_role = granted_role;
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
490 challenge = params.code_challenge;
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
491 challenge_method = params.code_challenge_method;
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
492 id_token = id_token;
4670
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
493 });
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
494 if not ok then
5476
575f52b15f5a mod_http_oauth2: Return OAuth error for authz code store error
Kim Alvefur <zash@zash.se>
parents: 5475
diff changeset
495 return oauth_error("temporarily_unavailable");
4670
1b81b7269858 mod_http_oauth2: Gracefully handle cache write failure
Kim Alvefur <zash@zash.se>
parents: 4669
diff changeset
496 end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
497
5458
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5457
diff changeset
498 if redirect_uri == oob_uri then
5495
7998b49d6512 mod_http_oauth2: Create proper template for OOB code delivery
Kim Alvefur <zash@zash.se>
parents: 5480
diff changeset
499 return render_page(templates.oob, { client = client; authorization_code = code }, true);
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
500 elseif redirect_uri == device_uri then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
501 return render_page(templates.device, { client = client }, true);
5219
25e824f64fd3 mod_http_oauth2: Improve handling of redirect_uri matching and fallback
Matthew Wild <mwild1@gmail.com>
parents: 5218
diff changeset
502 elseif not redirect_uri then
5462
f6d8830a83fe mod_http_oauth2: Return proper OAuth error for invalid redirect URI
Kim Alvefur <zash@zash.se>
parents: 5461
diff changeset
503 return oauth_error("invalid_redirect_uri");
5188
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5187
diff changeset
504 end
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5187
diff changeset
505
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5187
diff changeset
506 local redirect = url.parse(redirect_uri);
7c531137a553 mod_http_oauth2: Implement OOB special redirect URI in code flow
Kim Alvefur <zash@zash.se>
parents: 5187
diff changeset
507
5513
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
508 local query = strict_formdecode(redirect.query);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
509 if type(query) ~= "table" then query = {}; end
5192
03aa9baa9ac3 mod_http_oauth2: Add support for 'iss' authz response parameter (RFC 9207)
Matthew Wild <mwild1@gmail.com>
parents: 5191
diff changeset
510 table.insert(query, { name = "code", value = code });
5207
c72e3b0914e8 mod_http_oauth: Factor out issuer URL calculation to a helper function
Matthew Wild <mwild1@gmail.com>
parents: 5206
diff changeset
511 table.insert(query, { name = "iss", value = get_issuer() });
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
512 if params.state then
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
513 table.insert(query, { name = "state", value = params.state });
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
514 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
515 redirect.query = http.formencode(query);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
516
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
517 return {
5210
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5209
diff changeset
518 status_code = 303;
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
519 headers = {
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
520 cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
521 pragma = "no-cache";
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
522 location = url.build(redirect);
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
523 };
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
524 }
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
525 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
526
5186
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
527 -- Implicit flow
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
528 function response_type_handlers.token(client, params, granted_jid)
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
529 local request_username, request_host = jid.split(granted_jid);
5256
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5255
diff changeset
530 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: 5255
diff changeset
531 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: 5255
diff changeset
532 end
44f7edd4f845 mod_http_oauth2: Reject non-local hosts in more code paths
Kim Alvefur <zash@zash.se>
parents: 5255
diff changeset
533 local granted_scopes, granted_role = filter_scopes(request_username, params.scope);
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
534 local token_info = new_access_token(granted_jid, granted_role, granted_scopes, client, nil);
5186
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
535
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
536 local redirect = url.parse(get_redirect_uri(client, params.redirect_uri));
5463
dacde53467f3 mod_http_oauth2: Proper OAuth error for invalid redirect URI in implicit flow too
Kim Alvefur <zash@zash.se>
parents: 5462
diff changeset
537 if not redirect then return oauth_error("invalid_redirect_uri"); end
5186
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
538 token_info.state = params.state;
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
539 redirect.fragment = http.formencode(token_info);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
540
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
541 return {
5210
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5209
diff changeset
542 status_code = 303;
5186
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
543 headers = {
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
544 cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
545 pragma = "no-cache";
5186
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
546 location = url.build(redirect);
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
547 };
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
548 }
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
549 end
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
550
6232
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
551 function grant_type_handlers.authorization_code(params, client)
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
552 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: 4237
diff changeset
553 if params.scope and params.scope ~= "" then
5450
d2594bbf7c36 mod_http_oauth2: Scope FIXMEs
Kim Alvefur <zash@zash.se>
parents: 5449
diff changeset
554 -- FIXME allow a subset of granted scopes
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
555 return oauth_error("invalid_scope", "unknown scope requested");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
556 end
5607
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5605
diff changeset
557 local code, err = codes:get("authorization_code:" .. params.client_id .. "#" .. params.code);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
558 if err then error(err); end
5214
d5492bc861f6 mod_http_oauth2: Remove authorization codes after use
Kim Alvefur <zash@zash.se>
parents: 5213
diff changeset
559 -- 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: 5213
diff changeset
560 -- prevent a second attempted use
5550
4fda06be6b08 mod_http_oauth2: Make note about handling repeated
Kim Alvefur <zash@zash.se>
parents: 5549
diff changeset
561 -- TODO if a second attempt *is* made, revoke any tokens issued
5607
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5605
diff changeset
562 codes:set("authorization_code:" .. params.client_id .. "#" .. params.code, nil);
4269
143515d0b212 mod_http_oauth2: Factor out authorization code validity decision
Kim Alvefur <zash@zash.se>
parents: 4265
diff changeset
563 if not code or type(code) ~= "table" or code_expired(code) then
4260
c539334dd01a mod_http_oauth2: Rescope oauth client config into users' storage
Kim Alvefur <zash@zash.se>
parents: 4259
diff changeset
564 module:log("debug", "authorization_code invalid or expired: %q", code);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
565 return oauth_error("invalid_client", "incorrect credentials");
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
566 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
567
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
568 -- 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: 5382
diff changeset
569 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: 5382
diff changeset
570 if not transform then
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
571 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: 5382
diff changeset
572 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: 5382
diff changeset
573 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: 5382
diff changeset
574 end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
575
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
576 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: 5278
diff changeset
577 end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
578
6241
b460b2a65f0b mod_http_oauth2: Remove now redundant client_id check from remaining grant handlers
Kim Alvefur <zash@zash.se>
parents: 6238
diff changeset
579 function grant_type_handlers.refresh_token(params, client)
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
580 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: 5278
diff changeset
581
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
582 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: 5278
diff changeset
583 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: 5278
diff changeset
584 return oauth_error("invalid_grant", "invalid refresh token");
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
585 end
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
586
5512
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5511
diff changeset
587 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: 5511
diff changeset
588 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: 5511
diff changeset
589 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: 5511
diff changeset
590 refresh_token_client.name, refresh_token_client.hash);
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5511
diff changeset
591 return oauth_error("unauthorized_client", "incorrect credentials");
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5511
diff changeset
592 end
1fbc8718bed6 mod_http_oauth2: Bind refresh tokens to client
Kim Alvefur <zash@zash.se>
parents: 5511
diff changeset
593
5446
dd7bddc87f98 mod_http_oauth2: Fix inclusion of role in refreshed access tokens
Kim Alvefur <zash@zash.se>
parents: 5445
diff changeset
594 local refresh_scopes = refresh_token_info.grant.data.oauth2_scopes;
5448
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
595
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
596 if params.scope then
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
597 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: 5447
diff changeset
598 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: 5447
diff changeset
599 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: 5447
diff changeset
600 return granted_scopes:contains(scope);
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
601 end):concat(" ");
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
602 end
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
603
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
604 local username = jid.split(refresh_token_info.jid);
5446
dd7bddc87f98 mod_http_oauth2: Fix inclusion of role in refreshed access tokens
Kim Alvefur <zash@zash.se>
parents: 5445
diff changeset
605 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: 5445
diff changeset
606
5279
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
607 -- new_access_token() requires the actual token
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
608 refresh_token_info.token = params.refresh_token;
2b858cccac8f mod_http_oauth2: Add support for refresh tokens
Matthew Wild <mwild1@gmail.com>
parents: 5278
diff changeset
609
5448
9d542e86e19a mod_http_oauth2: Allow requesting a subset of scopes on token refresh
Kim Alvefur <zash@zash.se>
parents: 5447
diff changeset
610 return json.encode(new_access_token(refresh_token_info.jid, role, new_scopes, client, nil, refresh_token_info));
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
611 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
612
6241
b460b2a65f0b mod_http_oauth2: Remove now redundant client_id check from remaining grant handlers
Kim Alvefur <zash@zash.se>
parents: 6238
diff changeset
613 grant_type_handlers[device_uri] = function(params, client)
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
614 if not params.device_code then return oauth_error("invalid_request", "missing 'device_code'"); end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
615
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
616 local code = codes:get("device_code:" .. params.client_id .. "#" .. params.device_code);
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
617 if type(code) ~= "table" or code_expired(code) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
618 return oauth_error("expired_token");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
619 elseif code.error then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
620 return code.error;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
621 elseif not code.granted_jid then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
622 return oauth_error("authorization_pending");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
623 end
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
624 codes:set("device_code:" .. params.client_id .. "#" .. params.device_code, nil);
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
625
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
626 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
627 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
628
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
629 -- 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: 5382
diff changeset
630
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
631 function verifier_transforms.plain(code_verifier)
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
632 -- code_challenge = code_verifier
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
633 return code_verifier;
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
634 end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
635
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
636 function verifier_transforms.S256(code_verifier)
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
637 -- code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
5391
4aedce4fb95d mod_http_oauth2: Fix accidental uppercase in invocation of hash function
Kim Alvefur <zash@zash.se>
parents: 5390
diff changeset
638 return code_verifier and b64url(hashes.sha256(code_verifier));
5383
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
639 end
df11a2cbc7b7 mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Kim Alvefur <zash@zash.se>
parents: 5382
diff changeset
640
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
641 -- 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: 5207
diff changeset
642 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: 5207
diff changeset
643
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
644 -- 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: 5207
diff changeset
645 -- 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: 5207
diff changeset
646 -- 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: 5207
diff changeset
647 -- (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: 5207
diff changeset
648 -- to one of them).
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
649 -- 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: 5207
diff changeset
650 -- 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: 5207
diff changeset
651 local function get_auth_state(request)
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
652 local form = request.method == "POST"
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
653 and request.body
5276
67777cb7353d mod_http_oauth2: Pedantic optimization
Kim Alvefur <zash@zash.se>
parents: 5273
diff changeset
654 and request.body ~= ""
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
655 and request.headers.content_type == "application/x-www-form-urlencoded"
5514
61b8d3eb91a4 mod_http_oauth2: Revert strict form check to allow consent of multiple scopes
Kim Alvefur <zash@zash.se>
parents: 5513
diff changeset
656 and http.formdecode(request.body);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
657
5277
a1055024b94e mod_http_oauth2: Stricten check of urlencoded form data
Kim Alvefur <zash@zash.se>
parents: 5276
diff changeset
658 if type(form) ~= "table" then return {}; end
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
659
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
660 if not form.user_token then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
661 -- First step: login
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
662 local username = encodings.stringprep.nodeprep(form.username);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
663 local password = encodings.stringprep.saslprep(form.password);
5752
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
664 -- Many things hooked to authentication-{success,failure} don't expect
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
665 -- non-XMPP sessions so here's something close enough...
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
666 local auth_event = {
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
667 session = {
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
668 type = "http";
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
669 ip = request.ip;
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
670 conn = request.conn;
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
671 username = username;
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
672 host = module.host;
5771
72799c330986 mod_http_oauth2: Add logger to "session" for auth event
Kim Alvefur <zash@zash.se>
parents: 5770
diff changeset
673 log = request.log;
5752
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
674 sasl_handler = { username = username; selected = "x-www-form" };
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
675 client_id = request.headers.user_agent;
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
676 };
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
677 };
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
678 if not (username and password) or not usermanager.test_password(username, module.host, password) then
5752
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
679 module:fire_event("authentication-failure", auth_event);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
680 return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
681 error = "Invalid username/password";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
682 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
683 end
5752
c27eaa7117d6 mod_http_oauth2: Fire authentication events on login form
Kim Alvefur <zash@zash.se>
parents: 5751
diff changeset
684 module:fire_event("authentication-success", auth_event);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
685 return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
686 user = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
687 username = username;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
688 host = module.host;
5830
b109773ce6fe mod_http_oauth2: Reuse JWT issuance time as substitute for auth time
Kim Alvefur <zash@zash.se>
parents: 5799
diff changeset
689 token = new_user_token({ username = username; host = module.host; amr = { "pwd" } });
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
690 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
691 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
692 elseif form.user_token and form.consent then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
693 -- Second step: consent
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
694 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: 5207
diff changeset
695 if not ok then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
696 return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
697 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: 5207
diff changeset
698 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
699 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
700
5447
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
701 local scopes = array():append(form):filter(function(field)
5424
b45d9a81b3da mod_http_oauth2: Revert role selector, going to try something else
Kim Alvefur <zash@zash.se>
parents: 5423
diff changeset
702 return field.name == "scope";
5447
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
703 end):pluck("value");
5271
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
704
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
705 user.token = form.user_token;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
706 return {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
707 user = user;
5447
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
708 scopes = scopes;
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
709 consent = form.consent == "granted";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
710 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
711 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
712
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
713 return {};
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
714 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
715
5222
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
716 local function get_request_credentials(request)
5224
cd5cf4cc6304 mod_http_oauth2: Fail early when no authorization header present
Matthew Wild <mwild1@gmail.com>
parents: 5223
diff changeset
717 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: 5223
diff changeset
718
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
719 local auth_type, auth_data = string.match(request.headers.authorization, "^(%S+)%s(.+)$");
5935
46394b327d17 mod_http_oauth2: Guard against malformed authorization header
Kim Alvefur <zash@zash.se>
parents: 5931
diff changeset
720 if not auth_type then return nil; end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
721
5931
ca3479c67e48 mod_http_oauth2: HTTP authentication schemes are case-insensitive
Kim Alvefur <zash@zash.se>
parents: 5858
diff changeset
722 -- As described in Section 2.3 of [RFC5234], the string Bearer is case-insensitive.
ca3479c67e48 mod_http_oauth2: HTTP authentication schemes are case-insensitive
Kim Alvefur <zash@zash.se>
parents: 5858
diff changeset
723 -- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-5.1.1
ca3479c67e48 mod_http_oauth2: HTTP authentication schemes are case-insensitive
Kim Alvefur <zash@zash.se>
parents: 5858
diff changeset
724 auth_type = auth_type:lower();
ca3479c67e48 mod_http_oauth2: HTTP authentication schemes are case-insensitive
Kim Alvefur <zash@zash.se>
parents: 5858
diff changeset
725
ca3479c67e48 mod_http_oauth2: HTTP authentication schemes are case-insensitive
Kim Alvefur <zash@zash.se>
parents: 5858
diff changeset
726 if auth_type == "basic" then
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
727 local creds = base64.decode(auth_data);
5222
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
728 if not creds then return; end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
729 local username, password = string.match(creds, "^([^:]+):(.*)$");
5222
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
730 if not username then return; end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
731 return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
732 type = "basic";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
733 username = username;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
734 password = password;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
735 };
5931
ca3479c67e48 mod_http_oauth2: HTTP authentication schemes are case-insensitive
Kim Alvefur <zash@zash.se>
parents: 5858
diff changeset
736 elseif auth_type == "bearer" then
5222
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
737 return {
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
738 type = "bearer";
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
739 bearer_token = auth_data;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
740 };
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
741 end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
742
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
743 return nil;
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
744 end
578a72982bb2 mod_http_oauth2: Separate extracting credentials from requests and verifying
Matthew Wild <mwild1@gmail.com>
parents: 5221
diff changeset
745
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
746 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: 3919
diff changeset
747 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: 3919
diff changeset
748
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
749 function grant_type_handlers.password(params)
6244
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
750 local request_jid = params.username;
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
751 if not request_jid then
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
752 return oauth_error("invalid_request", "missing 'username' (JID)");
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
753 end
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
754 local request_password = params.password
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
755 if not request_password then
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
756 return oauth_error("invalid_request", "missing 'password'");
b7eb7d256939 mod_http_oauth2: Return instead of throwing errors
Kim Alvefur <zash@zash.se>
parents: 6243
diff changeset
757 end
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
758 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: 3919
diff changeset
759 if params.scope then
5450
d2594bbf7c36 mod_http_oauth2: Scope FIXMEs
Kim Alvefur <zash@zash.se>
parents: 5449
diff changeset
760 -- TODO shouldn't we support scopes / roles here?
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
761 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: 3919
diff changeset
762 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
763 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: 3919
diff changeset
764 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: 3919
diff changeset
765 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
766 if request_password == component_secret then
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
767 local granted_jid = jid.join(request_username, request_host, request_resource);
5254
b0ccdd12a70d mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
Kim Alvefur <zash@zash.se>
parents: 5252
diff changeset
768 return json.encode(new_access_token(granted_jid, nil, nil, nil));
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
769 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
770 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: 3919
diff changeset
771 end
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
772
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
773 -- TODO How would this make sense with components?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
774 -- Have an admin authenticate maybe?
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
775 response_type_handlers.code = nil;
5186
fa3059e653fa mod_http_oauth2: Implement the Implicit flow
Kim Alvefur <zash@zash.se>
parents: 5185
diff changeset
776 response_type_handlers.token = nil;
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
777 grant_type_handlers.authorization_code = nil;
3920
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
778 end
cf92e3b30c18 mod_http_oauth2: Use component_secret setting as password on Components
Kim Alvefur <zash@zash.se>
parents: 3919
diff changeset
779
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
780 local function render_error(err)
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
781 return render_page(templates.error, { error = err });
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
782 end
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
783
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
784 -- 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: 5207
diff changeset
785 -- 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: 5207
diff changeset
786 -- 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: 5207
diff changeset
787 -- 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: 5207
diff changeset
788 -- error directly to the user-agent.
5477
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5476
diff changeset
789 local function error_response(request, redirect_uri, err)
6263
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
790 if not redirect_uri or redirect_uri == oob_uri then
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
791 return render_error(err);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
792 end
6263
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
793 local params = strict_formdecode(request.url.query);
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
794 if redirect_uri == device_uri then
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
795 local is_device, device_state = verify_device_token(params.state);
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
796 if is_device then
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
797 local device_code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
798 local code = codes:get("device_code:" .. params.client_id .. "#" .. device_code);
6264
e1c54de06905 mod_http_oauth2: Handle case of device state having expired
Kim Alvefur <zash@zash.se>
parents: 6263
diff changeset
799 if type(code) == "table" then
e1c54de06905 mod_http_oauth2: Handle case of device state having expired
Kim Alvefur <zash@zash.se>
parents: 6263
diff changeset
800 code.error = err;
e1c54de06905 mod_http_oauth2: Handle case of device state having expired
Kim Alvefur <zash@zash.se>
parents: 6263
diff changeset
801 code.expires = os.time() + 60;
e1c54de06905 mod_http_oauth2: Handle case of device state having expired
Kim Alvefur <zash@zash.se>
parents: 6263
diff changeset
802 codes:set("device_code:" .. params.client_id .. "#" .. device_code, code);
e1c54de06905 mod_http_oauth2: Handle case of device state having expired
Kim Alvefur <zash@zash.se>
parents: 6263
diff changeset
803 end
6263
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
804 end
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
805 return render_error(err);
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
806 end
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
807 local redirect_query = url.parse(redirect_uri);
5229
c24a622a7b85 mod_http_oauth2: Fix appending of query parts in error redirects
Kim Alvefur <zash@zash.se>
parents: 5228
diff changeset
808 local sep = redirect_query.query and "&" or "?";
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
809 redirect_uri = redirect_uri
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
810 .. sep .. http.formencode(err.extra.oauth2_response)
6263
aae94f82c56e mod_http_oauth2: Refactor to return all errors to Device clients
Kim Alvefur <zash@zash.se>
parents: 6248
diff changeset
811 .. "&" .. http.formencode({ state = params.state, iss = get_issuer() });
5799
c75328aeaba3 mod_http_oauth2: Reduce log level for error delivery via redirect
Kim Alvefur <zash@zash.se>
parents: 5798
diff changeset
812 module:log("debug", "Sending error response to client via redirect to %s", redirect_uri);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
813 return {
5210
898575a0c6f3 mod_http_oauth2: Switch to '303 See Other' redirects
Matthew Wild <mwild1@gmail.com>
parents: 5209
diff changeset
814 status_code = 303;
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
815 headers = {
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
816 cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
817 pragma = "no-cache";
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
818 location = redirect_uri;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
819 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
820 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
821 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
822
5549
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
823 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: 5548
diff changeset
824 "authorization_code";
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
825 "refresh_token";
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
826 device_uri;
5549
01a0b67a9afd mod_http_oauth2: Add TODO about disabling password grant
Kim Alvefur <zash@zash.se>
parents: 5548
diff changeset
827 })
5614
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5608
diff changeset
828 if allowed_grant_type_handlers:contains("device_code") then
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5608
diff changeset
829 -- expand short form because that URI is long
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5608
diff changeset
830 module:log("debug", "Expanding %q to %q in '%s'", "device_code", device_uri, "allowed_oauth2_grant_types");
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5608
diff changeset
831 allowed_grant_type_handlers:remove("device_code");
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5608
diff changeset
832 allowed_grant_type_handlers:add(device_uri);
7565298aa197 mod_http_oauth2: Allow a shorter form of the device grant in config
Kim Alvefur <zash@zash.se>
parents: 5608
diff changeset
833 end
5187
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
834 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: 5186
diff changeset
835 if not allowed_grant_type_handlers:contains(handler_type) then
5230
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
836 module:log("debug", "Grant type %q disabled", handler_type);
5187
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
837 grant_type_handlers[handler_type] = nil;
5230
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
838 else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
839 module:log("debug", "Grant type %q enabled", handler_type);
5187
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
840 end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
841 end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
842
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
843 -- "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: 5186
diff changeset
844 local allowed_response_type_handlers = module:get_option_set("allowed_oauth2_response_types", {"code"})
5198
2e8a7a0f932d mod_http_oauth2: Fix response type config
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
845 for handler_type in pairs(response_type_handlers) do
2e8a7a0f932d mod_http_oauth2: Fix response type config
Kim Alvefur <zash@zash.se>
parents: 5196
diff changeset
846 if not allowed_response_type_handlers:contains(handler_type) then
5230
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
847 module:log("debug", "Response type %q disabled", handler_type);
5231
bef543068077 mod_http_oauth2: Fix to disable disabled response handlers correctly
Kim Alvefur <zash@zash.se>
parents: 5230
diff changeset
848 response_type_handlers[handler_type] = nil;
5230
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
849 else
ac252db71027 mod_http_oauth2: Log flows enabled and disabled
Kim Alvefur <zash@zash.se>
parents: 5229
diff changeset
850 module:log("debug", "Response type %q enabled", handler_type);
5187
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
851 end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
852 end
6a3c1febd7be mod_http_oauth2: Add settings for allowed grant and response types
Kim Alvefur <zash@zash.se>
parents: 5186
diff changeset
853
5716
426c42c11f89 mod_http_oauth2: Make defaults more secure
Kim Alvefur <zash@zash.se>
parents: 5715
diff changeset
854 local allowed_challenge_methods = module:get_option_set("allowed_oauth2_code_challenge_methods", { "S256" })
5384
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
855 for handler_type in pairs(verifier_transforms) do
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
856 if not allowed_challenge_methods:contains(handler_type) then
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
857 module:log("debug", "Challenge method %q disabled", handler_type);
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
858 verifier_transforms[handler_type] = nil;
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
859 else
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
860 module:log("debug", "Challenge method %q enabled", handler_type);
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
861 end
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
862 end
b40f29ec391a mod_http_oauth2: Allow configuring PKCE challenge methods
Kim Alvefur <zash@zash.se>
parents: 5383
diff changeset
863
6238
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
864 local function array_contains(haystack, needle)
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
865 for _, item in ipairs(haystack) do
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
866 if item == needle then
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
867 return true
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
868 end
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
869 end
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
870 return false
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
871 end
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
872
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
873 function handle_token_grant(event)
5223
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
874 local credentials = get_request_credentials(event.request);
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
875
3934
469408682152 mod_http_oauth2: Set content type on successful repsponses (fixes #1501)
Kim Alvefur <zash@zash.se>
parents: 3920
diff changeset
876 event.response.headers.content_type = "application/json";
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
877 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: 5502
diff changeset
878 event.response.headers.pragma = "no-cache";
5513
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
879 local params = strict_formdecode(event.request.body);
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
880 if not params then
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
881 return oauth_error("invalid_request", "Could not parse request body as 'application/x-www-form-urlencoded'");
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
882 end
5223
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
883
5225
3439eb37f23b mod_http_oauth2: token endpoint: handle missing credentials
Matthew Wild <mwild1@gmail.com>
parents: 5224
diff changeset
884 if credentials and credentials.type == "basic" then
5385
544b92750a2a mod_http_oauth2: Advertise supported token endpoint auth methods
Kim Alvefur <zash@zash.se>
parents: 5384
diff changeset
885 -- client_secret_basic converted internally to client_secret_post
5223
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
886 params.client_id = http.urldecode(credentials.username);
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
887 params.client_secret = http.urldecode(credentials.password);
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
888 end
8b2a36847912 mod_http_oauth2: Support HTTP Basic auth on token endpoint
Matthew Wild <mwild1@gmail.com>
parents: 5222
diff changeset
889
6232
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
890 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
891 if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
892
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
893 local client = check_client(params.client_id);
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
894 if not client then
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
895 return oauth_error("invalid_client", "incorrect credentials");
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
896 end
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
897
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
898 if not verify_client_secret(params.client_id, params.client_secret) then
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
899 module:log("debug", "client_secret mismatch");
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
900 return oauth_error("invalid_client", "incorrect credentials");
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
901 end
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
902
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
903
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
904 local grant_type = params.grant_type
6238
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
905 if not array_contains(client.grant_types or { "authorization_code" }, grant_type) then
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
906 return oauth_error("invalid_request", "'grant_type' not registered");
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
907 end
6237
9d88c3d9eea5 mod_http_oauth2: Enforce the registered grant types
Kim Alvefur <zash@zash.se>
parents: 6234
diff changeset
908
6238
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
909 local grant_handler = grant_type_handlers[grant_type];
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
910 if not grant_handler then
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
911 return oauth_error("invalid_request", "'grant_type' not available");
6237
9d88c3d9eea5 mod_http_oauth2: Enforce the registered grant types
Kim Alvefur <zash@zash.se>
parents: 6234
diff changeset
912 end
9d88c3d9eea5 mod_http_oauth2: Enforce the registered grant types
Kim Alvefur <zash@zash.se>
parents: 6234
diff changeset
913
6232
2505542c6c50 mod_http_oauth2: Deduplicate client authentication
Kim Alvefur <zash@zash.se>
parents: 6207
diff changeset
914 return grant_handler(params, client);
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
915 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
916
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
917 local function handle_authorization_request(event)
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
918 local request = event.request;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
919
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
920 -- Directly returning errors to the user before we have a validated client object
4258
cc712899becd mod_http_oauth2: Unpack event object to improve readability
Kim Alvefur <zash@zash.se>
parents: 4257
diff changeset
921 if not request.url.query then
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
922 return render_error(oauth_error("invalid_request", "Missing query parameters"));
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
923 end
5513
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
924 local params = strict_formdecode(request.url.query);
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
925 if not params then
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
926 return render_error(oauth_error("invalid_request", "Invalid query parameters"));
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
927 end
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
928
5471
d4d333cb75b2 mod_http_oauth2: Clarify some error messages
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
929 if not params.client_id then
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
930 return render_error(oauth_error("invalid_request", "Missing 'client_id' parameter"));
5471
d4d333cb75b2 mod_http_oauth2: Clarify some error messages
Kim Alvefur <zash@zash.se>
parents: 5470
diff changeset
931 end
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
932
5510
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
933 local client = check_client(params.client_id);
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
934
5510
a49d73e4262e mod_http_oauth2: Add client verification wrapper function
Kim Alvefur <zash@zash.se>
parents: 5509
diff changeset
935 if not client then
5472
b80b6947b079 mod_http_oauth2: Always show early errors to user
Kim Alvefur <zash@zash.se>
parents: 5471
diff changeset
936 return render_error(oauth_error("invalid_request", "Invalid 'client_id' parameter"));
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
937 end
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
938
5477
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5476
diff changeset
939 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: 5476
diff changeset
940 if not redirect_uri then
5475
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5474
diff changeset
941 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: 5474
diff changeset
942 end
022733437fef mod_http_oauth2: Validate redirect_uri before using it for error redirects
Kim Alvefur <zash@zash.se>
parents: 5474
diff changeset
943 -- 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: 5474
diff changeset
944
6238
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
945 local response_type = params.response_type;
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
946 if not array_contains(client.response_types or { "code" }, response_type) then
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
947 return error_response(request, redirect_uri, oauth_error("invalid_client", "'response_type' not registered"));
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
948 end
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
949 if not allowed_response_type_handlers:contains(response_type) then
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
950 return error_response(request, redirect_uri, oauth_error("unsupported_response_type", "'response_type' not allowed"));
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
951 end
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
952 local response_handler = response_type_handlers[response_type];
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
953 if not response_handler then
7b3316ac24b3 mod_http_oauth2: Refactor client grant and response type checks
Kim Alvefur <zash@zash.se>
parents: 6237
diff changeset
954 return error_response(request, redirect_uri, oauth_error("unsupported_response_type"));
5405
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5404
diff changeset
955 end
c7a5caad28ef mod_http_oauth2: Enforce response type encoded in client_id
Kim Alvefur <zash@zash.se>
parents: 5404
diff changeset
956
5447
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
957 local requested_scopes = parse_scopes(params.scope or "");
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
958 if client.scope then
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
959 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: 5446
diff changeset
960 requested_scopes:filter(function(scope)
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
961 return client_scopes:contains(scope);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
962 end);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
963 end
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
964
5518
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
965 -- The 'prompt' parameter from OpenID Core
5715
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
966 local prompt = set.new(parse_scopes(respect_prompt and params.prompt or "select_account login consent"));
5518
d87d0e4a8516 mod_http_oauth2: Validate the OpenID 'prompt' parameter
Kim Alvefur <zash@zash.se>
parents: 5514
diff changeset
967
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
968 local auth_state = get_auth_state(request);
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
969 if not auth_state.user then
5715
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
970 if not prompt:contains("login") then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
971 -- Currently no cookies or such are used, so login is required every time.
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
972 return error_response(request, redirect_uri, oauth_error("login_required"));
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
973 end
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
974
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
975 -- Render login page
5466
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5465
diff changeset
976 local extra = {};
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5465
diff changeset
977 if params.login_hint then
5764
87920d436cb4 mod_http_oauth2: Handle login_hint without @hostpart
Kim Alvefur <zash@zash.se>
parents: 5752
diff changeset
978 extra.username_hint = (jid.prepped_split(params.login_hint) or encodings.stringprep.nodeprep(params.login_hint));
5715
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
979 elseif not prompt:contains("select_account") then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
980 -- TODO If the login page is split into account selection followed by login
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
981 -- (e.g. password), and then the account selection could be skipped iff the
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
982 -- 'login_hint' parameter is present.
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
983 return error_response(request, redirect_uri, oauth_error("account_selection_required"));
5466
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5465
diff changeset
984 end
398d936e77fb mod_http_oauth2: Add support for the OpenID 'login_hint' parameter
Kim Alvefur <zash@zash.se>
parents: 5465
diff changeset
985 return render_page(templates.login, { state = auth_state; client = client; extra = extra });
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
986 elseif auth_state.consent == nil then
5447
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
987 local scopes, roles = split_scopes(requested_scopes);
5452
b071d8ee6555 mod_http_oauth2: Show only roles the user can use in consent dialog
Kim Alvefur <zash@zash.se>
parents: 5451
diff changeset
988 roles = user_assumable_roles(auth_state.user.username, roles);
5715
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
989
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
990 if not prompt:contains("consent") then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
991 local grants = tokens.get_user_grants(auth_state.user.username);
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
992 local matching_grant;
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
993 if grants then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
994 for grant_id, grant in pairs(grants) do
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
995 if grant.data and grant.data.oauth2_client and grant.data.oauth2_client.hash == client.client_hash then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
996 if set.new(parse_scopes(grant.data.oauth2_scopes)) == set.new(scopes+roles) then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
997 matching_grant = grant_id;
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
998 break
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
999 end
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1000 end
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1001 end
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1002 end
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1003
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1004 if not matching_grant then
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1005 return error_response(request, redirect_uri, oauth_error("consent_required"));
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1006 else
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1007 -- Consent for these scopes already granted to this exact client, continue
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1008 auth_state.scopes = scopes + roles;
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1009 auth_state.consent = "granted";
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1010 end
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1011
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1012 else
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1013 -- Render consent page
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1014 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true);
8488ebde5739 mod_http_oauth2: Skip consent screen if requested by client and same scopes already granted
Kim Alvefur <zash@zash.se>
parents: 5682
diff changeset
1015 end
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1016 elseif not auth_state.consent then
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1017 -- Notify client of rejection
5477
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5476
diff changeset
1018 return error_response(request, redirect_uri, oauth_error("access_denied"));
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1019 end
5271
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
1020 -- else auth_state.consent == true
3a1df3adad0c mod_http_oauth2: Allow user to decide which requested scopes to grant
Kim Alvefur <zash@zash.se>
parents: 5268
diff changeset
1021
5447
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1022 local granted_scopes = auth_state.scopes
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1023 if client.scope then
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1024 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: 5446
diff changeset
1025 granted_scopes:filter(function(scope)
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1026 return client_scopes:contains(scope);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1027 end);
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1028 end
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1029
aa4828f040c5 mod_http_oauth2: Enforce client scope restrictions in authorization
Kim Alvefur <zash@zash.se>
parents: 5446
diff changeset
1030 params.scope = granted_scopes:concat(" ");
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1031
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
1032 local user_jid = jid.join(auth_state.user.username, module.host);
5262
e73f364b5624 mod_http_oauth2: Rename oauth client credential related functions
Kim Alvefur <zash@zash.se>
parents: 5259
diff changeset
1033 local client_secret = make_client_secret(params.client_id);
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
1034 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: 5256
diff changeset
1035 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: 5256
diff changeset
1036 iss = get_issuer();
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
1037 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: 5256
diff changeset
1038 aud = params.client_id;
5830
b109773ce6fe mod_http_oauth2: Reuse JWT issuance time as substitute for auth time
Kim Alvefur <zash@zash.se>
parents: 5799
diff changeset
1039 auth_time = auth_state.user.iat;
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
1040 nonce = params.nonce;
6248
28fd42866be9 mod_http_oauth2: Add comment referencing RFC 8176
Kim Alvefur <zash@zash.se>
parents: 6247
diff changeset
1041 amr = auth_state.user.amr; -- RFC 8176: Authentication Method Reference Values
5257
b2120fb4a279 mod_http_oauth2: Implement and return ID Token in authorization code flow
Kim Alvefur <zash@zash.se>
parents: 5256
diff changeset
1042 });
5468
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5467
diff changeset
1043 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: 5467
diff changeset
1044 if errors.is_err(ret) then
5477
5986e0edd7a3 mod_http_oauth2: Use validated redirect URI when returning errors to client
Kim Alvefur <zash@zash.se>
parents: 5476
diff changeset
1045 return error_response(request, redirect_uri, ret);
5468
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5467
diff changeset
1046 end
14b5446e22e1 mod_http_oauth2: Fix returning errors from response handlers
Kim Alvefur <zash@zash.se>
parents: 5467
diff changeset
1047 return ret;
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
1048 end
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
1049
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1050 local function handle_device_authorization_request(event)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1051 local request = event.request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1052
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1053 local credentials = get_request_credentials(request);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1054
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1055 local params = strict_formdecode(request.body);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1056 if not params then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1057 return render_error(oauth_error("invalid_request", "Invalid query parameters"));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1058 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1059
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1060 if credentials and credentials.type == "basic" then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1061 -- client_secret_basic converted internally to client_secret_post
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1062 params.client_id = http.urldecode(credentials.username);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1063 local client_secret = http.urldecode(credentials.password);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1064
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1065 if not verify_client_secret(params.client_id, client_secret) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1066 module:log("debug", "client_secret mismatch");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1067 return oauth_error("invalid_client", "incorrect credentials");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1068 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1069 else
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1070 return 401;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1071 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1072
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1073 local client = check_client(params.client_id);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1074
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1075 if not client then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1076 return render_error(oauth_error("invalid_request", "Invalid 'client_id' parameter"));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1077 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1078
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1079 if not set.new(client.grant_types):contains(device_uri) then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1080 return render_error(oauth_error("invalid_client", "Client not registered for device authorization grant"));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1081 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1082
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1083 local requested_scopes = parse_scopes(params.scope or "");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1084 if client.scope then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1085 local client_scopes = set.new(parse_scopes(client.scope));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1086 requested_scopes:filter(function(scope)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1087 return client_scopes:contains(scope);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1088 end);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1089 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1090
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1091 -- TODO better code generator, this one should be easy to type from a
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1092 -- screen onto a phone
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1093 local user_code = (id.tiny() .. "-" .. id.tiny()):upper();
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1094 local collisions = 0;
5607
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5605
diff changeset
1095 while codes:get("authorization_code:" .. device_uri .. "#" .. user_code) do
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1096 collisions = collisions + 1;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1097 if collisions > 10 then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1098 return oauth_error("temporarily_unavailable");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1099 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1100 user_code = (id.tiny() .. "-" .. id.tiny()):upper();
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1101 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1102 -- device code should be derivable after consent but not guessable by the user
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1103 local device_code = b64url(hashes.hmac_sha256(verification_key, user_code));
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1104 local verification_uri = module:http_url() .. "/device";
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1105 local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1106
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
1107 local expires = os.time() + 600;
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
1108 local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = expires });
5607
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5605
diff changeset
1109 local uc_ok = codes:set("user_code:" .. user_code,
5629
ef0a283507c9 mod_http_oauth2: Make storage of various code more consistent
Kim Alvefur <zash@zash.se>
parents: 5628
diff changeset
1110 { user_code = user_code; expires = expires; client_id = params.client_id;
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1111 scope = requested_scopes:concat(" ") });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1112 if not dc_ok or not uc_ok then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1113 return oauth_error("temporarily_unavailable");
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1114 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1115
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1116 return {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1117 headers = { content_type = "application/json"; cache_control = "no-store"; pragma = "no-cache" };
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1118 body = json.encode {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1119 device_code = device_code;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1120 user_code = user_code;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1121 verification_uri = verification_uri;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1122 verification_uri_complete = verification_uri_complete;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1123 expires_in = 600;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1124 interval = 5;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1125 };
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1126 }
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1127 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1128
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1129 local function handle_device_verification_request(event)
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1130 local request = event.request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1131 local params = strict_formdecode(request.url.query);
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1132 if not params or not params.user_code then
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1133 return render_page(templates.device, { client = false });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1134 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1135
5607
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5605
diff changeset
1136 local device_info = codes:get("user_code:" .. params.user_code);
ad9b8f659c96 mod_http_oauth2: Namespace the various codes to minimize confusion
Kim Alvefur <zash@zash.se>
parents: 5605
diff changeset
1137 if not device_info or code_expired(device_info) or not codes:set("user_code:" .. params.user_code, nil) then
5608
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5607
diff changeset
1138 return render_page(templates.device, {
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5607
diff changeset
1139 client = false;
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5607
diff changeset
1140 error = oauth_error("expired_token", "Incorrect or expired code");
1893ae742f66 mod_http_oauth2: Show errors on device flow user code entry page
Kim Alvefur <zash@zash.se>
parents: 5607
diff changeset
1141 });
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1142 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1143
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1144 return {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1145 status_code = 303;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1146 headers = {
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1147 location = module:http_url() .. "/authorize" .. "?" .. http.formencode({
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1148 client_id = device_info.client_id;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1149 redirect_uri = device_uri;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1150 response_type = "code";
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1151 scope = device_info.scope;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1152 state = new_device_token({ user_code = params.user_code });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1153 });
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1154 };
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1155 }
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1156 end
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1157
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1158 local function handle_introspection_request(event)
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1159 local request = event.request;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1160 local credentials = get_request_credentials(request);
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1161 if not credentials or credentials.type ~= "basic" then
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1162 event.response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1163 return 401;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1164 end
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1165 -- OAuth "client" credentials
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1166 if not verify_client_secret(credentials.username, credentials.password) then
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1167 return 401;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1168 end
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1169
5681
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1170 local client = check_client(credentials.username);
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1171 if not client then
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1172 return 401;
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1173 end
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1174
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1175 local form_data = http.formdecode(request.body or "=");
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1176 local token = form_data.token;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1177 if not token then
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1178 return 400;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1179 end
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1180
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1181 local token_info = tokens.get_token_info(form_data.token);
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1182 if not token_info then
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1183 return { headers = { content_type = "application/json" }; body = json.encode { active = false } };
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1184 end
5681
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1185 local token_client = token_info.grant.data.oauth2_client;
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1186 if not token_client or token_client.hash ~= client.client_hash then
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1187 return 403;
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1188 end
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1189
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1190 return {
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1191 headers = { content_type = "application/json" };
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1192 body = json.encode {
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1193 active = true;
5858
761142ee0ff2 mod_http_oauth2: Reflect changes to defaults etc
Kim Alvefur <zash@zash.se>
parents: 5830
diff changeset
1194 client_id = credentials.username; -- Verified via client hash
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1195 username = jid.node(token_info.jid);
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1196 scope = token_info.grant.data.oauth2_scopes;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1197 token_type = purpose_map[token_info.purpose];
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1198 exp = token.expires;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1199 iat = token.created;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1200 sub = url.build({ scheme = "xmpp"; path = token_info.jid });
5681
8cb3da7df521 mod_http_oauth2: Restrict introspection to clients own tokens
Kim Alvefur <zash@zash.se>
parents: 5680
diff changeset
1201 aud = credentials.username;
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1202 iss = get_issuer();
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1203 jti = token_info.id;
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1204 };
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1205 };
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1206 end
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1207
5682
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1208 -- RFC 7009 says that the authorization server should validate that only the client that a token was issued to should be able to revoke it. However
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1209 -- this would prevent someone who comes across a leaked token from doing the responsible thing and revoking it, so this is not enforced by default.
5626
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1210 local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false);
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1211
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1212 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: 4340
diff changeset
1213 local request, response = event.request, event.response;
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
1214 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: 5502
diff changeset
1215 response.headers.pragma = "no-cache";
5682
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1216 local credentials = get_request_credentials(request);
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1217 if credentials then
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1218 if credentials.type ~= "basic" then
5265
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5264
diff changeset
1219 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: 5264
diff changeset
1220 return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5264
diff changeset
1221 end
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5264
diff changeset
1222 -- OAuth "client" credentials
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5264
diff changeset
1223 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: 5264
diff changeset
1224 return 401;
f845c218e52c mod_http_oauth2: Allow revoking a token without OAuth client credentials
Kim Alvefur <zash@zash.se>
parents: 5264
diff changeset
1225 end
5626
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1226 -- TODO check that it's their token I guess?
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1227 elseif strict_auth_revoke then
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1228 -- Why require auth to revoke a leaked token?
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1229 response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
a44af1b646f5 mod_http_oauth2: Optionally enforce authentication on revocation endpoint
Kim Alvefur <zash@zash.se>
parents: 5619
diff changeset
1230 return 401;
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1231 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1232
5513
0005d4201030 mod_http_oauth2: Reject duplicate form-urlencoded parameters
Kim Alvefur <zash@zash.se>
parents: 5512
diff changeset
1233 local form_data = strict_formdecode(event.request.body);
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1234 if not form_data or not form_data.token then
5267
60e0bc35de33 mod_http_oauth2: Relax payload content type checking in revocation
Kim Alvefur <zash@zash.se>
parents: 5266
diff changeset
1235 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: 5266
diff changeset
1236 return 415;
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1237 end
5682
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1238
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1239 if credentials then
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1240 local client = check_client(credentials.username);
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1241 if not client then
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1242 return 401;
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1243 end
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1244 local token_info = tokens.get_token_info(form_data.token);
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1245 if not token_info then
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1246 return 404;
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1247 end
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1248 local token_client = token_info.grant.data.oauth2_client;
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1249 if not token_client or token_client.hash ~= client.client_hash then
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1250 return 403;
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1251 end
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1252 end
527c747711f3 mod_http_oauth2: Limit revocation to clients own tokens in strict mode
Kim Alvefur <zash@zash.se>
parents: 5681
diff changeset
1253
4370
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1254 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: 4340
diff changeset
1255 if not ok then
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1256 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: 4340
diff changeset
1257 return 500;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1258 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1259 return 200;
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1260 end
dee6b5098278 mod_http_oauth2: Add endpoint to revoke a key (RFC 7009 kinda)
Matthew Wild <mwild1@gmail.com>
parents: 4340
diff changeset
1261
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1262 local registration_schema = {
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1263 title = "OAuth 2.0 Dynamic Client Registration Protocol";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1264 description = "This endpoint allows dynamically registering an OAuth 2.0 client.";
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1265 type = "object";
5237
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5236
diff changeset
1266 required = {
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5236
diff changeset
1267 -- 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: 5236
diff changeset
1268 "client_name";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5236
diff changeset
1269 "client_uri";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5236
diff changeset
1270 -- 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: 5236
diff changeset
1271 "redirect_uris";
3354f943c1fa mod_http_oauth2: Require URL to client informational page in registration
Kim Alvefur <zash@zash.se>
parents: 5236
diff changeset
1272 };
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1273 properties = {
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1274 redirect_uris = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1275 title = "List of Redirect URIs";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1276 type = "array";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1277 minItems = 1;
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1278 uniqueItems = true;
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1279 items = {
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1280 title = "Redirect URI";
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1281 type = "string";
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1282 format = "uri";
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1283 examples = {
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1284 "https://app.example.com/redirect";
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1285 "http://localhost:8080/redirect";
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1286 "com.example.app:/redirect";
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1287 oob_uri;
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1288 device_uri;
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1289 };
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1290 };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1291 };
5377
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5375
diff changeset
1292 token_endpoint_auth_method = {
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1293 title = "Token Endpoint Authentication Method";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1294 description = "Authentication method the client intends to use. Recommended is `client_secret_basic`. \z
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1295 `none` is only allowed for use with the insecure Implicit flow.";
5377
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5375
diff changeset
1296 type = "string";
ca477408f90b mod_http_oauth2: Fix misplaced 'default' on wrong side of } in client registration schema
Kim Alvefur <zash@zash.se>
parents: 5375
diff changeset
1297 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: 5375
diff changeset
1298 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: 5375
diff changeset
1299 };
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1300 grant_types = {
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1301 title = "Grant Types";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1302 description = "List of grant types the client intends to use.";
5236
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5231
diff changeset
1303 type = "array";
5455
80a81e7f3c4e mod_http_oauth2: Require non-empty arrays in client registration
Kim Alvefur <zash@zash.se>
parents: 5454
diff changeset
1304 minItems = 1;
5456
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1305 uniqueItems = true;
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1306 items = {
5236
ff8623e2f9d9 mod_http_oauth2: Reorder client metadata validation schema
Kim Alvefur <zash@zash.se>
parents: 5231
diff changeset
1307 type = "string";
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1308 enum = {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1309 "authorization_code";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1310 "implicit";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1311 "password";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1312 "client_credentials";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1313 "refresh_token";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1314 "urn:ietf:params:oauth:grant-type:jwt-bearer";
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1315 "urn:ietf:params:oauth:grant-type:saml2-bearer";
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1316 device_uri;
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1317 };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1318 };
5366
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5365
diff changeset
1319 default = { "authorization_code" };
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1320 };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1321 application_type = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1322 title = "Application Type";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1323 description = "Determines which kinds of redirect URIs the client may register. \z
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1324 The value `web` limits the client to `https://` URLs with the same hostname as \z
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1325 in `client_uri` while the value `native` allows either loopback URLs like \z
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1326 `http://localhost:8080/` or application specific URIs like `com.example.app:/redirect`.";
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1327 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1328 enum = { "native"; "web" };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1329 default = "web";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1330 };
5456
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1331 response_types = {
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1332 title = "Response Types";
5456
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1333 type = "array";
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1334 uniqueItems = true;
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1335 items = { type = "string"; enum = { "code"; "token" } };
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1336 default = { "code" };
9008aea491bf mod_http_oauth2: Reject duplicate list items in client registration
Kim Alvefur <zash@zash.se>
parents: 5455
diff changeset
1337 };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1338 client_name = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1339 title = "Client Name";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1340 description = "Human-readable name of the client, presented to the user in the consent dialog.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1341 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1342 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1343 client_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1344 title = "Client URL";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1345 description = "Should be an link to a page with information about the client. \z
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1346 The hostname in this URL must be the same as in every other '_uri' property.";
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1347 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1348 format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1349 pattern = "^https:";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1350 examples = { "https://app.example.com/" };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1351 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1352 logo_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1353 title = "Logo URL";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1354 description = "URL to the clients logotype (not currently used).";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1355 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1356 format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1357 pattern = "^https:";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1358 examples = { "https://app.example.com/appicon.png" };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1359 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1360 scope = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1361 title = "Scopes";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1362 description = "Space-separated list of scopes the client promises to restrict itself to.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1363 type = "string";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1364 examples = { "openid xmpp" };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1365 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1366 contacts = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1367 title = "Contact Addresses";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1368 description = "Addresses, typically email or URLs where the client developers can be contacted.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1369 type = "array";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1370 minItems = 1;
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1371 items = { type = "string"; format = "email" };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1372 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1373 tos_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1374 title = "Terms of Service URL";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1375 description = "Link to Terms of Service for the client, presented to the user in the consent dialog. \z
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1376 MUST be a `https://` URL with hostname matching that of `client_uri`.";
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1377 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1378 format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1379 pattern = "^https:";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1380 examples = { "https://app.example.com/tos.html" };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1381 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1382 policy_uri = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1383 title = "Privacy Policy URL";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1384 description = "Link to a Privacy Policy for the client. MUST be a `https://` URL with hostname matching that of `client_uri`.";
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1385 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1386 format = "uri";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1387 pattern = "^https:";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1388 examples = { "https://app.example.com/policy.pdf" };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1389 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1390 software_id = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1391 title = "Software ID";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1392 description = "Unique identifier for the client software, common for all instances. Typically an UUID.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1393 type = "string";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1394 format = "uuid";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1395 };
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1396 software_version = {
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1397 title = "Software Version";
5605
17aa3bac7f3a mod_http_oauth2: Improve a description in schema
Kim Alvefur <zash@zash.se>
parents: 5598
diff changeset
1398 description = "Version of the client software being registered. \z
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1399 E.g. to allow revoking all related tokens in the event of a security incident.";
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1400 type = "string";
5797
03477980f1a9 mod_http_oauth2: Improve registration schema documentation parts
Kim Alvefur <zash@zash.se>
parents: 5796
diff changeset
1401 examples = { "2.3.1" };
5598
b496ebc12aed mod_http_oauth2: Add titles and descriptions to registration schema
Kim Alvefur <zash@zash.se>
parents: 5589
diff changeset
1402 };
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1403 };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1404 }
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1405
5554
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1406 -- Limit per-locale fields to allowed locales, partly to keep size of client_id
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1407 -- down, partly because we don't yet use them for anything.
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1408 -- Only relevant for user-visible strings and URIs.
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1409 if allowed_locales[1] then
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1410 local props = registration_schema.properties;
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1411 for _, locale in ipairs(allowed_locales) do
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1412 props["client_name#" .. locale] = props["client_name"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1413 props["client_uri#" .. locale] = props["client_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1414 props["logo_uri#" .. locale] = props["logo_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1415 props["tos_uri#" .. locale] = props["tos_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1416 props["policy_uri#" .. locale] = props["policy_uri"];
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1417 end
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1418 end
90449babaa48 mod_http_oauth2: Make allowed locales configurable
Kim Alvefur <zash@zash.se>
parents: 5553
diff changeset
1419
5367
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1420 local function redirect_uri_allowed(redirect_uri, client_uri, app_type)
5956
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
1421 local uri = strict_url_parse(redirect_uri);
5767
a967bb4972c5 mod_http_oauth2: Reject unparsable URLs
Kim Alvefur <zash@zash.se>
parents: 5766
diff changeset
1422 if not uri then
a967bb4972c5 mod_http_oauth2: Reject unparsable URLs
Kim Alvefur <zash@zash.se>
parents: 5766
diff changeset
1423 return false;
a967bb4972c5 mod_http_oauth2: Reject unparsable URLs
Kim Alvefur <zash@zash.se>
parents: 5766
diff changeset
1424 end
5457
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5456
diff changeset
1425 if not uri.scheme then
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5456
diff changeset
1426 return false; -- no relative URLs
9156a4754466 mod_http_oauth2: Reject relative redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5456
diff changeset
1427 end
5367
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1428 if app_type == "native" then
5458
813fe4f76286 mod_http_oauth2: Do minimal validation of private-use URI schemes
Kim Alvefur <zash@zash.se>
parents: 5457
diff changeset
1429 return uri.scheme == "http" and loopbacks:contains(uri.host) or redirect_uri == oob_uri or uri.scheme:find(".", 1, true) ~= nil;
5367
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1430 elseif app_type == "web" then
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1431 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: 5366
diff changeset
1432 end
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1433 end
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1434
5259
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1435 function create_client(client_metadata)
5766
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1436 local valid, validation_errors = schema.validate(registration_schema, client_metadata);
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1437 if not valid then
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1438 return nil, errors.new({
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1439 type = "modify";
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1440 condition = "bad-request";
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1441 code = 400;
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1442 text = "Failed schema validation.";
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1443 extra = {
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1444 oauth2_response = {
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1445 error = "invalid_request";
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1446 error_description = "Client registration data failed schema validation."; -- TODO Generate from validation_errors?
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1447 -- JSON Schema Output Format
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1448 -- https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-basic
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1449 valid = false;
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1450 errors = validation_errors;
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1451 };
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1452 };
b8a2b3ebe792 mod_http_oauth2: Return validation output added in trunk rev 72d7830505f0
Kim Alvefur <zash@zash.se>
parents: 5764
diff changeset
1453 });
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1454 end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1455
5956
97375a78d2b5 mod_http_oauth2: Reject URLs with 'userinfo' part (thanks mimi89999)
Kim Alvefur <zash@zash.se>
parents: 5935
diff changeset
1456 local client_uri = strict_url_parse(client_metadata.client_uri);
5958
5f8a306c8306 mod_http_oauth2: Require a stringprepped host part of URLs
Kim Alvefur <zash@zash.se>
parents: 5957
diff changeset
1457 if not client_uri or client_uri.scheme ~= "https" or not client_uri.host or loopbacks:contains(client_uri.host) then
5633
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1458 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri");
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1459 end
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1460
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1461 if not client_metadata.application_type and redirect_uri_allowed(client_metadata.redirect_uris[1], client_uri, "native") then
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1462 client_metadata.application_type = "native";
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1463 -- else defaults to "web"
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1464 end
dd2079b3dec6 mod_http_oauth2: Allow omitting application type for native apps
Kim Alvefur <zash@zash.se>
parents: 5629
diff changeset
1465
5366
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5365
diff changeset
1466 -- Fill in default values
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5365
diff changeset
1467 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: 5365
diff changeset
1468 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: 5365
diff changeset
1469 client_metadata[propname] = propspec.default;
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5365
diff changeset
1470 end
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5365
diff changeset
1471 end
db4c66a1d24b mod_http_oauth2: Fill in some client metadata defaults
Kim Alvefur <zash@zash.se>
parents: 5365
diff changeset
1472
5559
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1473 -- MUST ignore any metadata that it does not understand
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1474 for propname in pairs(client_metadata) do
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1475 if not registration_schema.properties[propname] then
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1476 client_metadata[propname] = nil;
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1477 end
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1478 end
d7fb8b266663 mod_http_oauth2: Strip unknown client metadata
Kim Alvefur <zash@zash.se>
parents: 5554
diff changeset
1479
5239
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5237
diff changeset
1480 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do
5367
93d445b26063 mod_http_oauth2: Validate redirect URI depending on application type
Kim Alvefur <zash@zash.se>
parents: 5366
diff changeset
1481 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then
5402
fbf3ede7541b mod_http_oauth2: More appropriate error conditions in client validation
Kim Alvefur <zash@zash.se>
parents: 5401
diff changeset
1482 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI.");
5242
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
1483 end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
1484 end
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
1485
5244
fa7bd721a3f6 mod_http_oauth2: Fix validation of informative URIs
Kim Alvefur <zash@zash.se>
parents: 5243
diff changeset
1486 for field, prop_schema in pairs(registration_schema.properties) do
5246
fd0d25b42cd9 mod_http_oauth2: Validate all URIs against client_uri in client registration
Kim Alvefur <zash@zash.se>
parents: 5245
diff changeset
1487 if field ~= "client_uri" and prop_schema.format == "uri" and client_metadata[field] then
5403
c574aaaa4d57 mod_http_oauth2: Simplify validation of various URIs
Kim Alvefur <zash@zash.se>
parents: 5402
diff changeset
1488 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: 5402
diff changeset
1489 return nil, oauth_error("invalid_client_metadata", "Invalid, insecure or inappropriate informative URI");
5242
4746609a6656 mod_http_oauth2: Validate that informative URLs match the redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5241
diff changeset
1490 end
5239
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5237
diff changeset
1491 end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5237
diff changeset
1492 end
8620a635106e mod_http_oauth2: Validate basic URI syntax of redirect URIs
Kim Alvefur <zash@zash.se>
parents: 5237
diff changeset
1493
5406
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5405
diff changeset
1494 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: 5405
diff changeset
1495 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: 5405
diff changeset
1496
6243
5b269511ade7 mod_http_oauth2: Forbid inclusion of disabled grant and response types
Kim Alvefur <zash@zash.se>
parents: 6242
diff changeset
1497 if not (grant_types - allowed_grant_type_handlers):empty() then
5b269511ade7 mod_http_oauth2: Forbid inclusion of disabled grant and response types
Kim Alvefur <zash@zash.se>
parents: 6242
diff changeset
1498 return nil, oauth_error("invalid_client_metadata", "Disallowed 'grant_types' specified");
5b269511ade7 mod_http_oauth2: Forbid inclusion of disabled grant and response types
Kim Alvefur <zash@zash.se>
parents: 6242
diff changeset
1499 elseif not (response_types - allowed_response_type_handlers):empty() then
5b269511ade7 mod_http_oauth2: Forbid inclusion of disabled grant and response types
Kim Alvefur <zash@zash.se>
parents: 6242
diff changeset
1500 return nil, oauth_error("invalid_client_metadata", "Disallowed 'response_types' specified");
5b269511ade7 mod_http_oauth2: Forbid inclusion of disabled grant and response types
Kim Alvefur <zash@zash.se>
parents: 6242
diff changeset
1501 end
5b269511ade7 mod_http_oauth2: Forbid inclusion of disabled grant and response types
Kim Alvefur <zash@zash.se>
parents: 6242
diff changeset
1502
5406
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5405
diff changeset
1503 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: 5405
diff changeset
1504 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: 5405
diff changeset
1505 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: 5405
diff changeset
1506 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: 5405
diff changeset
1507 end
b86d80e21c60 mod_http_oauth2: Validate consistency of response and grant types
Kim Alvefur <zash@zash.se>
parents: 5405
diff changeset
1508
5770
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1509 if client_metadata.token_endpoint_auth_method ~= "none" then
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1510 -- Ensure that each client_id JWT with a client_secret is unique.
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1511 -- A short ID along with the issued at timestamp should be sufficient to
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1512 -- rule out brute force attacks.
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1513 -- Not needed for public clients without a secret, but those are expected
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1514 -- to be uncommon since they can only do the insecure implicit flow.
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1515 client_metadata.nonce = id.short();
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1516 end
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1517
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1518 -- Do we want to keep everything?
5459
260a859be86a mod_http_oauth2: Rename variables to improve clarity
Kim Alvefur <zash@zash.se>
parents: 5458
diff changeset
1519 local client_id = sign_client(client_metadata);
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1520
5221
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5219
diff changeset
1521 client_metadata.client_id = client_id;
22483cfce3ce mod_http_oauth2: Reflect ALL attributes of the client registration
Matthew Wild <mwild1@gmail.com>
parents: 5219
diff changeset
1522 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: 5219
diff changeset
1523
5407
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
1524 if client_metadata.token_endpoint_auth_method ~= "none" then
5770
990c6adc4407 mod_http_oauth2: Move some code earlier
Kim Alvefur <zash@zash.se>
parents: 5767
diff changeset
1525 local client_secret = make_client_secret(client_id);
5407
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
1526 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: 5406
diff changeset
1527 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: 5406
diff changeset
1528
149634647b48 mod_http_oauth2: Don't issue client_secret when not using authentication
Kim Alvefur <zash@zash.se>
parents: 5406
diff changeset
1529 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: 5406
diff changeset
1530 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: 5406
diff changeset
1531 end
5202
b81fd0d22c66 mod_http_oauth2: Calculate client secret expiry in registration response
Kim Alvefur <zash@zash.se>
parents: 5201
diff changeset
1532 end
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1533
5259
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1534 return client_metadata;
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1535 end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1536
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1537 local function handle_register_request(event)
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1538 local request = event.request;
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1539 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: 5258
diff changeset
1540 if err then
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1541 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: 5258
diff changeset
1542 end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1543
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1544 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: 5258
diff changeset
1545 if err then return err end
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1546
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1547 return {
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1548 status_code = 201;
5509
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
1549 headers = {
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
1550 cache_control = "no-store";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
1551 pragma = "no-cache";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
1552 content_type = "application/json";
ae007be8a6bd mod_http_oauth2: Add Cache-Control and Pragma headers per by RFC 6749
Kim Alvefur <zash@zash.se>
parents: 5502
diff changeset
1553 };
5259
8fba651b10ef mod_http_oauth2: Refactor to allow reuse of OAuth client creation
Kim Alvefur <zash@zash.se>
parents: 5258
diff changeset
1554 body = json.encode(response);
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1555 };
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1556 end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1557
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1558 if not registration_key then
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1559 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: 5192
diff changeset
1560 handle_authorization_request = nil
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1561 handle_register_request = nil
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1562 handle_device_authorization_request = nil
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1563 handle_device_verification_request = nil
5193
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1564 end
2bb29ece216b mod_http_oauth2: Implement stateless dynamic client registration
Kim Alvefur <zash@zash.se>
parents: 5192
diff changeset
1565
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1566 local function handle_userinfo_request(event)
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1567 local request = event.request;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1568 local credentials = get_request_credentials(request);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1569 if not credentials or not credentials.bearer_token then
5336
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5335
diff changeset
1570 module:log("debug", "Missing credentials for UserInfo endpoint: %q", credentials)
5335
53c6f49dcbb8 mod_http_oauth2: Correct error code when missing credentials for userinfo
Kim Alvefur <zash@zash.se>
parents: 5280
diff changeset
1571 return 401;
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1572 end
5336
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5335
diff changeset
1573 local token_info,err = tokens.get_token_info(credentials.bearer_token);
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1574 if not token_info then
5336
77ac04bd2f65 mod_http_oauth2: Add some debug logging for UserInfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5335
diff changeset
1575 module:log("debug", "UserInfo query failed token validation: %s", err)
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1576 return 403;
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1577 end
5337
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1578 local scopes = set.new()
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1579 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: 5336
diff changeset
1580 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: 5336
diff changeset
1581 else
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1582 module:log("debug", "token_info = %q", token_info)
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1583 end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1584
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1585 if not scopes:contains("openid") then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1586 module:log("debug", "Missing the 'openid' scope in %q", scopes)
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1587 -- The 'openid' scope is required for access to this endpoint.
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1588 return 403;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1589 end
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1590
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1591 local user_info = {
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1592 iss = get_issuer();
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1593 sub = url.build({ scheme = "xmpp"; path = token_info.jid });
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1594 }
5337
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1595
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1596 local token_claims = set.intersection(openid_claims, scopes);
5375
8b7d97f0ae8a mod_http_oauth2: Fix to include "openid" scope in discovery metadata
Kim Alvefur <zash@zash.se>
parents: 5367
diff changeset
1597 token_claims:remove("openid"); -- that's "iss" and "sub" above
5337
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1598 if not token_claims:empty() then
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1599 -- Another module can do that
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1600 module:fire_event("token/userinfo", {
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1601 token = token_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1602 claims = token_claims;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1603 username = jid.split(token_info.jid);
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1604 userinfo = user_info;
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1605 });
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1606 end
8d8e85d6dc91 mod_http_oauth2: Support OpenID UserInfo claims
Kim Alvefur <zash@zash.se>
parents: 5336
diff changeset
1607
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1608 return {
5258
9629971e307f mod_http_oauth2: Fix userinfo status code off-by-one
Kim Alvefur <zash@zash.se>
parents: 5257
diff changeset
1609 status_code = 200;
5228
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1610 headers = { content_type = "application/json" };
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1611 body = json.encode(user_info);
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1612 };
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1613 end
77cd01af06a9 mod_http_oauth2: Implement the OpenID userinfo endpoint
Kim Alvefur <zash@zash.se>
parents: 5225
diff changeset
1614
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1615 module:depends("http");
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1616 module:provides("http", {
5480
5108f63e762b mod_http_oauth2: Allow CORS for browser clients
Kim Alvefur <zash@zash.se>
parents: 5479
diff changeset
1617 cors = { enabled = true; credentials = true };
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1618 route = {
5382
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5378
diff changeset
1619 -- 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: 5378
diff changeset
1620 -- 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: 5378
diff changeset
1621
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5378
diff changeset
1622 -- Step 1. Create OAuth client
6179
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1623 ["GET /register"] = { headers = { content_type = "application/schema+json" }; body = json.encode(registration_schema) };
5382
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5378
diff changeset
1624 ["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: 5378
diff changeset
1625
5589
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1626 -- Device flow
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1627 ["POST /device"] = handle_device_authorization_request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1628 ["GET /device"] = handle_device_verification_request;
7040d0772758 mod_http_oauth2: Implement RFC 8628 Device Authorization Grant
Kim Alvefur <zash@zash.se>
parents: 5580
diff changeset
1629
5382
12498c0d705f mod_http_oauth2: Reorder routes into order they happen in OAuth 2.0
Kim Alvefur <zash@zash.se>
parents: 5378
diff changeset
1630 -- Step 2. User-facing login and consent view
4256
c4b9d4ba839b mod_http_oauth2: Authorization code flow
Kim Alvefur <zash@zash.se>
parents: 4237
diff changeset
1631 ["GET /authorize"] = handle_authorization_request;
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1632 ["POST /authorize"] = handle_authorization_request;
5548
fd3c12c40cd9 mod_http_oauth2: Disable CORS for authorization endpoint
Kim Alvefur <zash@zash.se>
parents: 5547
diff changeset
1633 ["OPTIONS /authorize"] = { status_code = 403; body = "" };
5245
e22cae58141d mod_http_oauth2: Organize HTTP routes with comments
Kim Alvefur <zash@zash.se>
parents: 5244
diff changeset
1634
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1635 -- Optional static content for templates
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1636 ["GET /style.css"] = templates.css and {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1637 headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1638 ["Content-Type"] = "text/css";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1639 };
5642
7c105277a9ca mod_http_oauth2: Remove broken in-CSS templating
Kim Alvefur <zash@zash.se>
parents: 5633
diff changeset
1640 body = templates.css;
5208
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1641 } or nil;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1642 ["GET /script.js"] = templates.js and {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1643 headers = {
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1644 ["Content-Type"] = "text/javascript";
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1645 };
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1646 body = templates.js;
aaa64c647e12 mod_http_oauth2: Add authentication, consent and error pages
Matthew Wild <mwild1@gmail.com>
parents: 5207
diff changeset
1647 } or nil;
5393
9b9d612f9083 mod_http_oauth2: Add way to retrieve registration schema
Kim Alvefur <zash@zash.se>
parents: 5392
diff changeset
1648
6179
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1649 -- Step 3. User is redirected to the 'redirect_uri' along with an
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1650 -- authorization code. In the insecure 'implicit' flow, the access token
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1651 -- is delivered here.
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1652
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1653 -- Step 4. Retrieve access token using the code.
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1654 ["POST /token"] = handle_token_grant;
5396
ac7c5669e5f5 mod_http_oauth2: Return status 405 for GET to endpoints without GET handler
Kim Alvefur <zash@zash.se>
parents: 5394
diff changeset
1655 ["GET /token"] = function() return 405; end;
6179
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1656
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1657 -- Step 4 is later repeated using the refresh token to get new access tokens.
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1658
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1659 -- Get info about a token
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1660 ["POST /introspect"] = handle_introspection_request;
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1661 ["GET /introspect"] = function() return 405; end;
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1662
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1663 -- Get info about the user, used for OpenID Connect
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1664 ["GET /userinfo"] = handle_userinfo_request;
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1665
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1666 -- Step 5. Revoke token (access or refresh)
a1a33f0f6f6e mod_http_oauth2: Reorder HTTP handler (noop)
Kim Alvefur <zash@zash.se>
parents: 6009
diff changeset
1667 ["POST /revoke"] = handle_revocation_request;
5396
ac7c5669e5f5 mod_http_oauth2: Return status 405 for GET to endpoints without GET handler
Kim Alvefur <zash@zash.se>
parents: 5394
diff changeset
1668 ["GET /revoke"] = function() return 405; end;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1669 };
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1670 });
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1671
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1672 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
1673
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1674 module:hook_object_event(http_server, "http-error", function (event)
4276
ec33b3b1136c mod_http_oauth2: Fix passing OAuth-specific error details
Kim Alvefur <zash@zash.se>
parents: 4272
diff changeset
1675 local oauth2_response = event.error and event.error.extra and event.error.extra.oauth2_response;
3903
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1676 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
1677 return;
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1678 end
cfeb93b80621 mod_http_oauth2: OAuth2 API (work in progress for developers only)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1679 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
1680 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
1681 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
1682 end, 5);
5189
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1683
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1684 -- OIDC Discovery
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1685
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1686 function get_authorization_server_metadata()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1687 if authorization_server_metadata then
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1688 return authorization_server_metadata;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1689 end
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1690 authorization_server_metadata = {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1691 -- 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: 5501
diff changeset
1692 issuer = get_issuer();
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1693 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: 5501
diff changeset
1694 token_endpoint = handle_token_grant and module:http_url() .. "/token" or nil;
6247
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1695 jwks_uri = nil; -- REQUIRED in OpenID Discovery but not in OAuth 2.0 Metadata
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1696 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: 5501
diff changeset
1697 scopes_supported = usermanager.get_all_roles
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1698 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: 5501
diff changeset
1699 response_types_supported = array(it.keys(response_type_handlers));
6247
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1700 response_modes_supported = array(it.keys(response_type_handlers)):map(tmap { token = "fragment"; code = "query" });
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1701 grant_types_supported = array(it.keys(grant_type_handlers));
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1702 token_endpoint_auth_methods_supported = array({ "client_secret_basic"; "client_secret_post"; "none" });
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1703 token_endpoint_auth_signing_alg_values_supported = nil;
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1704 service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html");
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1705 ui_locales_supported = allowed_locales[1] and allowed_locales;
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1706 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: 5501
diff changeset
1707 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: 5501
diff changeset
1708 revocation_endpoint = handle_revocation_request and module:http_url() .. "/revoke" or nil;
6247
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1709 revocation_endpoint_auth_methods_supported = array({ "client_secret_basic"; "client_secret_post"; "none" });
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1710 revocation_endpoint_auth_signing_alg_values_supported = nil;
5680
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1711 introspection_endpoint = handle_introspection_request and module:http_url() .. "/introspect";
b43c989fb69c mod_http_oauth2: Implement introspection endpoint
Kim Alvefur <zash@zash.se>
parents: 5665
diff changeset
1712 introspection_endpoint_auth_methods_supported = nil;
6247
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1713 introspection_endpoint_auth_signing_alg_values_supported = nil;
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1714 code_challenge_methods_supported = array(it.keys(verifier_transforms));
6247
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1715
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1716 -- RFC 8628: OAuth 2.0 Device Authorization Grant
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1717 device_authorization_endpoint = handle_device_authorization_request and module:http_url() .. "/device";
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1718
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1719 -- RFC 9207: OAuth 2.0 Authorization Server Issuer Identification
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1720 authorization_response_iss_parameter_supported = true;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1721
6247
7cf1fcac9b94 mod_http_oauth2: Reorder metadata by source
Kim Alvefur <zash@zash.se>
parents: 6245
diff changeset
1722 -- OpenID Connect Discovery 1.0
6009
277ccafb4826 mod_http_oauth2: Fix check for userinfo endpoint handler
Kim Alvefur <zash@zash.se>
parents: 5961
diff changeset
1723 userinfo_endpoint = handle_userinfo_request and module:http_url() .. "/userinfo" or nil;
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1724 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: 5501
diff changeset
1725 }
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1726 return authorization_server_metadata;
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1727 end
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1728
5189
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1729 module:provides("http", {
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1730 name = "oauth2-discovery";
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1731 default_path = "/.well-known/oauth-authorization-server";
5480
5108f63e762b mod_http_oauth2: Allow CORS for browser clients
Kim Alvefur <zash@zash.se>
parents: 5479
diff changeset
1732 cors = { enabled = true };
5189
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1733 route = {
5502
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1734 ["GET"] = function()
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1735 return {
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1736 headers = { content_type = "application/json" };
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1737 body = json.encode(get_authorization_server_metadata());
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1738 }
fd4d89a5b8db mod_http_oauth2: Add provisions for dynamically adding simple scopes
Kim Alvefur <zash@zash.se>
parents: 5501
diff changeset
1739 end
5189
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1740 };
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1741 });
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1742
4ee8eb1134a8 mod_http_oauth2: Add OIDC discovery endpoint (thanks Zash)
Matthew Wild <mwild1@gmail.com>
parents: 5188
diff changeset
1743 module:shared("tokenauth/oauthbearer_config").oidc_discovery_url = module:http_url("oauth2-discovery", "/.well-known/oauth-authorization-server");