comparison mod_http_oauth2/mod_http_oauth2.lua @ 4340:7cd3b7ec59e9

mod_http_oauth2: Rudimentary support for scopes (but not really) We don't support limiting access, but this change will inform the client what permissions the created token has (e.g. is the user an admin or not). There is some work in progress on real scope support.
author Matthew Wild <mwild1@gmail.com>
date Sat, 16 Jan 2021 19:47:22 +0000
parents ec33b3b1136c
children dee6b5098278
comparison
equal deleted inserted replaced
4339:3b7847c9bd26 4340:7cd3b7ec59e9
12 12
13 local tokens = module:depends("tokenauth"); 13 local tokens = module:depends("tokenauth");
14 14
15 local clients = module:open_store("oauth2_clients", "map"); 15 local clients = module:open_store("oauth2_clients", "map");
16 16
17 local function filter_scopes(request_jid, requested_scope_string) --luacheck: ignore 212/requested_scope_string
18 -- We currently don't really support scopes, so override
19 -- to whatever real permissions the user has
20 if usermanager.is_admin(request_jid, module.host) then
21 return "prosody:scope:admin";
22 end
23 return "prosody:scope:default";
24 end
25
17 local function code_expired(code) 26 local function code_expired(code)
18 return os.difftime(os.time(), code.issued) > 120; 27 return os.difftime(os.time(), code.issued) > 120;
19 end 28 end
20 29
21 local codes = cache.new(10000, function (_, code) 30 local codes = cache.new(10000, function (_, code)
45 local token = tokens.create_jid_token(token_jid, token_jid, scope, ttl); 54 local token = tokens.create_jid_token(token_jid, token_jid, scope, ttl);
46 return { 55 return {
47 token_type = "bearer"; 56 token_type = "bearer";
48 access_token = token; 57 access_token = token;
49 expires_in = ttl; 58 expires_in = ttl;
59 scope = scope;
50 -- TODO: include refresh_token when implemented 60 -- TODO: include refresh_token when implemented
51 }; 61 };
52 end 62 end
53 63
54 local grant_type_handlers = {}; 64 local grant_type_handlers = {};
56 66
57 function grant_type_handlers.password(params) 67 function grant_type_handlers.password(params)
58 local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)")); 68 local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)"));
59 local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'")); 69 local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'"));
60 local request_username, request_host, request_resource = jid.prepped_split(request_jid); 70 local request_username, request_host, request_resource = jid.prepped_split(request_jid);
61 if params.scope and params.scope ~= "" then 71
62 return oauth_error("invalid_scope", "unknown scope requested");
63 end
64 if not (request_username and request_host) or request_host ~= module.host then 72 if not (request_username and request_host) or request_host ~= module.host then
65 return oauth_error("invalid_request", "invalid JID"); 73 return oauth_error("invalid_request", "invalid JID");
66 end 74 end
67 if usermanager.test_password(request_username, request_host, request_password) then 75 if not usermanager.test_password(request_username, request_host, request_password) then
68 local granted_jid = jid.join(request_username, request_host, request_resource); 76 return oauth_error("invalid_grant", "incorrect credentials");
69 return json.encode(new_access_token(granted_jid, nil, nil)); 77 end
70 end 78
71 return oauth_error("invalid_grant", "incorrect credentials"); 79 local granted_jid = jid.join(request_username, request_host, request_resource);
80 local granted_scopes = filter_scopes(granted_jid, params.scope);
81 return json.encode(new_access_token(granted_jid, granted_scopes, nil));
72 end 82 end
73 83
74 function response_type_handlers.code(params, granted_jid) 84 function response_type_handlers.code(params, granted_jid)
75 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end 85 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
76 if not params.redirect_uri then return oauth_error("invalid_request", "missing 'redirect_uri'"); end 86 if not params.redirect_uri then return oauth_error("invalid_request", "missing 'redirect_uri'"); end
77 if params.scope and params.scope ~= "" then
78 return oauth_error("invalid_scope", "unknown scope requested");
79 end
80 87
81 local client_owner, client_host, client_id = jid.prepped_split(params.client_id); 88 local client_owner, client_host, client_id = jid.prepped_split(params.client_id);
82 if client_host ~= module.host then 89 if client_host ~= module.host then
83 return oauth_error("invalid_client", "incorrect credentials"); 90 return oauth_error("invalid_client", "incorrect credentials");
84 end 91 end
86 if err then error(err); end 93 if err then error(err); end
87 if not client then 94 if not client then
88 return oauth_error("invalid_client", "incorrect credentials"); 95 return oauth_error("invalid_client", "incorrect credentials");
89 end 96 end
90 97
98 local granted_scopes = filter_scopes(granted_jid, params.scope);
99
91 local code = uuid.generate(); 100 local code = uuid.generate();
92 assert(codes:set(params.client_id .. "#" .. code, {issued = os.time(); granted_jid = granted_jid})); 101 assert(codes:set(params.client_id .. "#" .. code, {
102 issued = os.time();
103 granted_jid = granted_jid;
104 granted_scopes = granted_scopes;
105 }));
93 106
94 local redirect = url.parse(params.redirect_uri); 107 local redirect = url.parse(params.redirect_uri);
95 local query = http.formdecode(redirect.query or ""); 108 local query = http.formdecode(redirect.query or "");
96 if type(query) ~= "table" then query = {}; end 109 if type(query) ~= "table" then query = {}; end
97 table.insert(query, { name = "code", value = code }) 110 table.insert(query, { name = "code", value = code })
139 module:log("debug", "authorization_code invalid or expired: %q", code); 152 module:log("debug", "authorization_code invalid or expired: %q", code);
140 return oauth_error("invalid_client", "incorrect credentials"); 153 return oauth_error("invalid_client", "incorrect credentials");
141 end 154 end
142 assert(codes:set(client_owner, client_id .. "#" .. params.code, nil)); 155 assert(codes:set(client_owner, client_id .. "#" .. params.code, nil));
143 156
144 return json.encode(new_access_token(code.granted_jid, nil, nil)); 157 return json.encode(new_access_token(code.granted_jid, code.granted_scopes, nil));
145 end 158 end
146 159
147 local function check_credentials(request) 160 local function check_credentials(request)
148 local auth_type, auth_data = string.match(request.headers.authorization, "^(%S+)%s(.+)$"); 161 local auth_type, auth_data = string.match(request.headers.authorization, "^(%S+)%s(.+)$");
149 162