Mercurial > prosody-modules
comparison mod_http_oauth2/mod_http_oauth2.lua @ 5257:b2120fb4a279
mod_http_oauth2: Implement and return ID Token in authorization code flow
Is this OIDC?
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 16 Mar 2023 19:28:44 +0100 |
parents | 44f7edd4f845 |
children | 9629971e307f |
comparison
equal
deleted
inserted
replaced
5256:44f7edd4f845 | 5257:b2120fb4a279 |
---|---|
150 -- client needs to be revoked | 150 -- client needs to be revoked |
151 local function client_subset(client) | 151 local function client_subset(client) |
152 return { name = client.client_name; uri = client.client_uri }; | 152 return { name = client.client_name; uri = client.client_uri }; |
153 end | 153 end |
154 | 154 |
155 local function new_access_token(token_jid, role, scope, ttl, client) | 155 local function new_access_token(token_jid, role, scope, ttl, client, id_token) |
156 local token_data = {}; | 156 local token_data = {}; |
157 if client then | 157 if client then |
158 token_data.oauth2_client = client_subset(client); | 158 token_data.oauth2_client = client_subset(client); |
159 end | 159 end |
160 if next(token_data) == nil then | 160 if next(token_data) == nil then |
164 return { | 164 return { |
165 token_type = "bearer"; | 165 token_type = "bearer"; |
166 access_token = token; | 166 access_token = token; |
167 expires_in = ttl; | 167 expires_in = ttl; |
168 scope = scope; | 168 scope = scope; |
169 id_token = id_token; | |
169 -- TODO: include refresh_token when implemented | 170 -- TODO: include refresh_token when implemented |
170 }; | 171 }; |
171 end | 172 end |
172 | 173 |
173 local function get_redirect_uri(client, query_redirect_uri) -- record client, string : string | 174 local function get_redirect_uri(client, query_redirect_uri) -- record client, string : string |
202 return oauth_error("invalid_grant", "incorrect credentials"); | 203 return oauth_error("invalid_grant", "incorrect credentials"); |
203 end | 204 end |
204 | 205 |
205 local granted_jid = jid.join(request_username, request_host, request_resource); | 206 local granted_jid = jid.join(request_username, request_host, request_resource); |
206 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); | 207 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); |
207 return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, nil)); | 208 return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, nil, nil)); |
208 end | 209 end |
209 | 210 |
210 function response_type_handlers.code(client, params, granted_jid) | 211 function response_type_handlers.code(client, params, granted_jid, id_token) |
211 local request_username, request_host = jid.split(granted_jid); | 212 local request_username, request_host = jid.split(granted_jid); |
212 if not request_host or request_host ~= module.host then | 213 if not request_host or request_host ~= module.host then |
213 return oauth_error("invalid_request", "invalid JID"); | 214 return oauth_error("invalid_request", "invalid JID"); |
214 end | 215 end |
215 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); | 216 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); |
218 local ok = codes:set(params.client_id .. "#" .. code, { | 219 local ok = codes:set(params.client_id .. "#" .. code, { |
219 expires = os.time() + 600; | 220 expires = os.time() + 600; |
220 granted_jid = granted_jid; | 221 granted_jid = granted_jid; |
221 granted_scopes = granted_scopes; | 222 granted_scopes = granted_scopes; |
222 granted_role = granted_role; | 223 granted_role = granted_role; |
224 id_token = id_token; | |
223 }); | 225 }); |
224 if not ok then | 226 if not ok then |
225 return {status_code = 429}; | 227 return {status_code = 429}; |
226 end | 228 end |
227 | 229 |
266 local request_username, request_host = jid.split(granted_jid); | 268 local request_username, request_host = jid.split(granted_jid); |
267 if not request_host or request_host ~= module.host then | 269 if not request_host or request_host ~= module.host then |
268 return oauth_error("invalid_request", "invalid JID"); | 270 return oauth_error("invalid_request", "invalid JID"); |
269 end | 271 end |
270 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); | 272 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); |
271 local token_info = new_access_token(granted_jid, granted_role, granted_scopes, nil, client); | 273 local token_info = new_access_token(granted_jid, granted_role, granted_scopes, nil, client, nil); |
272 | 274 |
273 local redirect = url.parse(get_redirect_uri(client, params.redirect_uri)); | 275 local redirect = url.parse(get_redirect_uri(client, params.redirect_uri)); |
274 token_info.state = params.state; | 276 token_info.state = params.state; |
275 redirect.fragment = http.formencode(token_info); | 277 redirect.fragment = http.formencode(token_info); |
276 | 278 |
315 if not code or type(code) ~= "table" or code_expired(code) then | 317 if not code or type(code) ~= "table" or code_expired(code) then |
316 module:log("debug", "authorization_code invalid or expired: %q", code); | 318 module:log("debug", "authorization_code invalid or expired: %q", code); |
317 return oauth_error("invalid_client", "incorrect credentials"); | 319 return oauth_error("invalid_client", "incorrect credentials"); |
318 end | 320 end |
319 | 321 |
320 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, nil, client)); | 322 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, nil, client, code.id_token)); |
321 end | 323 end |
322 | 324 |
323 -- Used to issue/verify short-lived tokens for the authorization process below | 325 -- Used to issue/verify short-lived tokens for the authorization process below |
324 local new_user_token, verify_user_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 }); | 326 local new_user_token, verify_user_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 }); |
325 | 327 |
547 elseif not auth_state.consent then | 549 elseif not auth_state.consent then |
548 -- Notify client of rejection | 550 -- Notify client of rejection |
549 return error_response(request, oauth_error("access_denied")); | 551 return error_response(request, oauth_error("access_denied")); |
550 end | 552 end |
551 | 553 |
554 local user_jid = jid.join(auth_state.user.username, module.host); | |
555 local client_secret = make_secret(params.client_id); | |
556 local id_token_signer = jwt.new_signer("HS256", client_secret); | |
557 local id_token = id_token_signer({ | |
558 iss = get_issuer(); | |
559 sub = url.build({ scheme = "xmpp"; path = user_jid }); | |
560 aud = params.client_id; | |
561 nonce = params.nonce; | |
562 }); | |
552 local response_type = params.response_type; | 563 local response_type = params.response_type; |
553 local response_handler = response_type_handlers[response_type]; | 564 local response_handler = response_type_handlers[response_type]; |
554 if not response_handler then | 565 if not response_handler then |
555 return error_response(request, oauth_error("unsupported_response_type")); | 566 return error_response(request, oauth_error("unsupported_response_type")); |
556 end | 567 end |
557 return response_handler(client, params, jid.join(auth_state.user.username, module.host)); | 568 return response_handler(client, params, user_jid, id_token); |
558 end | 569 end |
559 | 570 |
560 local function handle_revocation_request(event) | 571 local function handle_revocation_request(event) |
561 local request, response = event.request, event.response; | 572 local request, response = event.request, event.response; |
562 if not request.headers.authorization then | 573 if not request.headers.authorization then |