comparison src/ur/openid.ur @ 10:194577b60771

Call user-specified function after authentication
author Adam Chlipala <adam@chlipala.net>
date Wed, 29 Dec 2010 14:38:56 -0500
parents 426dd5c88df1
children e637249abfd2
comparison
equal deleted inserted replaced
9:426dd5c88df1 10:194577b60771
180 newAssociation url alt.Atype alt.Stype 180 newAssociation url alt.Atype alt.Stype
181 | v => return v 181 | v => return v
182 182
183 fun eatFragment s = 183 fun eatFragment s =
184 case String.split s #"#" of 184 case String.split s #"#" of
185 Some (_, s') => s' 185 Some (s', _) => s'
186 | _ => s 186 | _ => s
187 187
188 datatype handle_result = HandleOk of {Endpoint : string, Typ : association_type, Key : string} | HandleError of string 188 datatype handle_result = HandleOk of {Endpoint : string, Typ : association_type, Key : string} | HandleError of string
189 189
190 fun verifyHandle os id = 190 fun verifyHandle os id =
191 ep <- discover (eatFragment id); 191 id' <- return (eatFragment id);
192 ep <- discover id';
192 case ep of 193 case ep of
193 None => return (HandleError "Discovery failed on returned endpoint") 194 None => return (HandleError ("Discovery failed on returned identifier: " ^ id'))
194 | Some ep => 195 | Some ep =>
195 case OpenidFfi.getOutput os "openid.assoc_handle" of 196 case OpenidFfi.getOutput os "openid.assoc_handle" of
196 None => return (HandleError "Missing association handle in response") 197 None => return (HandleError "Missing association handle in response")
197 | Some handle => 198 | Some handle =>
198 assoc <- oldAssociation ep; 199 assoc <- oldAssociation ep;
286 return (Some "Signatures don't match") 287 return (Some "Signatures don't match")
287 end 288 end
288 | Some (left, _) => return (Some ("openid.signed is missing required fields: " ^ show left)) 289 | Some (left, _) => return (Some ("openid.signed is missing required fields: " ^ show left))
289 end 290 end
290 291
291 fun returnTo (qs : option queryString) = 292 datatype authentication = AuthenticatedAs of string | Canceled | Failure of string
292 case qs of 293
293 None => error <xml>Empty query string for OpenID callback</xml> 294 fun authenticate after r =
294 | Some qs => 295 let
295 os <- OpenidFfi.indirect qs; 296 fun returnTo (qs : option queryString) =
296 case OpenidFfi.getOutput os "openid.error" of 297 case qs of
297 Some v => error <xml>Authentication failed: {[v]}</xml> 298 None => after (Failure "Empty query string for OpenID callback")
298 | None => 299 | Some qs =>
299 case OpenidFfi.getOutput os "openid.mode" of 300 os <- OpenidFfi.indirect qs;
300 None => error <xml>No <tt>openid.mode</tt> in response ({[qs]})</xml> 301 case OpenidFfi.getOutput os "openid.error" of
301 | Some mode => 302 Some v => after (Failure "Authentication failed: {[v]}")
302 case mode of 303 | None =>
303 "cancel" => error <xml>You canceled the authentication!</xml> 304 case OpenidFfi.getOutput os "openid.mode" of
304 | "id_res" => 305 None => after (Failure "No openid.mode in response")
305 (case OpenidFfi.getOutput os "openid.identity" of 306 | Some mode =>
306 None => error <xml>Missing identity in OP response</xml> 307 case mode of
307 | Some id => 308 "cancel" => after Canceled
308 errO <- verifyHandle os id; 309 | "id_res" =>
309 case errO of 310 (case OpenidFfi.getOutput os "openid.claimed_id" of
310 HandleError s => error <xml>{[s]}</xml> 311 None => after (Failure "Missing identity in OP response")
311 | HandleOk {Endpoint = ep, Typ = atype, Key = key} => 312 | Some id =>
312 errO <- verifyReturnTo os; 313 errO <- verifyHandle os id;
313 case errO of
314 Some s => error <xml>{[s]}</xml>
315 | None =>
316 errO <- verifyNonce os ep;
317 case errO of 314 case errO of
318 Some s => error <xml>{[s]}</xml> 315 HandleError s => after (Failure s)
319 | None => 316 | HandleOk {Endpoint = ep, Typ = atype, Key = key} =>
320 errO <- verifySig os atype key; 317 errO <- verifyReturnTo os;
321 case errO of 318 case errO of
322 Some s => error <xml>{[s]}</xml> 319 Some s => after (Failure s)
323 | None => return <xml>Identity: {[id]}</xml>) 320 | None =>
324 | _ => error <xml>Unexpected <tt>openid.mode</tt>: <tt>{[mode]}</tt></xml> 321 errO <- verifyNonce os ep;
325 322 case errO of
326 and verifyReturnTo os = 323 Some s => after (Failure s)
327 case OpenidFfi.getOutput os "openid.return_to" of 324 | None =>
328 None => return (Some "Missing return_to in OP response") 325 errO <- verifySig os atype key;
329 | Some rt => 326 case errO of
330 if rt <> show (effectfulUrl returnTo) then 327 Some s => after (Failure s)
331 return (Some "Wrong return_to in OP response") 328 | None => after (AuthenticatedAs id))
332 else 329 | _ => after (Failure ("Unexpected openid.mode: " ^ mode))
333 return None 330
334 331 and verifyReturnTo os =
335 fun authenticate atype stype id = 332 case OpenidFfi.getOutput os "openid.return_to" of
336 dy <- discover id; 333 None => return (Some "Missing return_to in OP response")
337 case dy of 334 | Some rt =>
338 None => return "Discovery failed" 335 if rt <> show (effectfulUrl returnTo) then
339 | Some dy => 336 return (Some "Wrong return_to in OP response")
340 assoc <- association atype stype dy; 337 else
341 case assoc of 338 return None
342 AssError msg => return ("Association failure: " ^ msg) 339 in
343 | AssAlternate _ => return "Association failure: server didn't accept its own alternate association modes" 340 dy <- discover r.Identifier;
344 | Association assoc => 341 case dy of
345 redirect (bless (dy ^ "?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=" 342 None => return "Discovery failed"
346 ^ id ^ "&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.assoc_handle=" 343 | Some dy =>
347 ^ assoc.Handle ^ "&openid.return_to=" ^ show (effectfulUrl returnTo))) 344 assoc <- association r.AssociationType r.AssociationSessionType dy;
345 case assoc of
346 AssError msg => return ("Association failure: " ^ msg)
347 | AssAlternate _ => return "Association failure: server didn't accept its own alternate association modes"
348 | Association assoc =>
349 redirect (bless (dy ^ "?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id="
350 ^ r.Identifier ^ "&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.assoc_handle="
351 ^ assoc.Handle ^ "&openid.return_to=" ^ show (effectfulUrl returnTo)))
352 end
348 353
349 task periodic 1 = fn () => 354 task periodic 1 = fn () =>
350 dml (DELETE FROM discoveries WHERE Expires < CURRENT_TIMESTAMP); 355 dml (DELETE FROM discoveries WHERE Expires < CURRENT_TIMESTAMP);
351 dml (DELETE FROM associations WHERE Expires < CURRENT_TIMESTAMP); 356 dml (DELETE FROM associations WHERE Expires < CURRENT_TIMESTAMP);
352 dml (DELETE FROM nonces WHERE Expires < CURRENT_TIMESTAMP) 357 dml (DELETE FROM nonces WHERE Expires < CURRENT_TIMESTAMP)