Mercurial > openid
comparison src/ur/openid.ur @ 13:de04a3fc6b72
Stateless verification worked
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Sun, 02 Jan 2011 10:11:38 -0500 |
parents | c778455fe570 |
children | 6b2a44da71b0 |
comparison
equal
deleted
inserted
replaced
12:c778455fe570 | 13:de04a3fc6b72 |
---|---|
28 OpenidFfi.addInput is "openid.ns" "http://specs.openid.net/auth/2.0"; | 28 OpenidFfi.addInput is "openid.ns" "http://specs.openid.net/auth/2.0"; |
29 return is | 29 return is |
30 | 30 |
31 datatype association_type = HMAC_SHA1 | HMAC_SHA256 | 31 datatype association_type = HMAC_SHA1 | HMAC_SHA256 |
32 datatype association_session_type = NoEncryption | DH_SHA1 | DH_SHA256 | 32 datatype association_session_type = NoEncryption | DH_SHA1 | DH_SHA256 |
33 datatype association_mode = | |
34 Stateless | |
35 | Stateful of {AssociationType : association_type, | |
36 AssociationSessionType : association_session_type} | |
33 | 37 |
34 table associations : { Endpoint : string, Handle : string, Typ : serialized association_type, Key : string, Expires : time } | 38 table associations : { Endpoint : string, Handle : string, Typ : serialized association_type, Key : string, Expires : time } |
35 PRIMARY KEY Endpoint | 39 PRIMARY KEY Endpoint |
36 | 40 |
37 datatype association = Association of {Handle : string, Typ : association_type, Key : string} | 41 datatype association = Association of {Handle : string, Typ : association_type, Key : string} |
189 fun eatFragment s = | 193 fun eatFragment s = |
190 case String.split s #"#" of | 194 case String.split s #"#" of |
191 Some (s', _) => s' | 195 Some (s', _) => s' |
192 | _ => s | 196 | _ => s |
193 | 197 |
194 datatype handle_result = HandleOk of {Endpoint : string, Typ : association_type, Key : string} | HandleError of string | 198 datatype handle_result = HandleOk of {Endpoint : string, Typ : association_type, Key : string} | NoAssociation of string | HandleError of string |
199 | |
200 datatype authentication = AuthenticatedAs of string | Canceled | Failure of string | |
195 | 201 |
196 fun verifyHandle os id = | 202 fun verifyHandle os id = |
197 id' <- return (eatFragment id); | 203 id' <- return (eatFragment id); |
198 ep <- discover id'; | 204 ep <- discover id'; |
199 case ep of | 205 case ep of |
202 case OpenidFfi.getOutput os "openid.assoc_handle" of | 208 case OpenidFfi.getOutput os "openid.assoc_handle" of |
203 None => return (HandleError "Missing association handle in response") | 209 None => return (HandleError "Missing association handle in response") |
204 | Some handle => | 210 | Some handle => |
205 assoc <- oldAssociation ep; | 211 assoc <- oldAssociation ep; |
206 case assoc of | 212 case assoc of |
207 None => return (HandleError "Couldn't find association handle") | 213 None => return (NoAssociation ep) |
208 | Some assoc => | 214 | Some assoc => |
209 if assoc.Handle <> handle then | 215 if assoc.Handle <> handle then |
210 return (HandleError "Association handles don't match") | 216 return (HandleError "Association handles don't match") |
211 else | 217 else |
212 return (HandleOk {Endpoint = ep, Typ = assoc.Typ, Key = assoc.Key}) | 218 return (HandleOk {Endpoint = ep, Typ = assoc.Typ, Key = assoc.Key}) |
219 | |
220 fun verifyStateless os ep id = | |
221 os' <- OpenidFfi.direct ep (OpenidFfi.remode os "check_authentication"); | |
222 case OpenidFfi.getOutput os' "error" of | |
223 Some msg => return (Failure ("Failure confirming message contents with OP: " ^ msg)) | |
224 | None => | |
225 case OpenidFfi.getOutput os' "is_valid" of | |
226 Some "true" => return (AuthenticatedAs id) | |
227 | _ => return (Failure "OP does not confirm message contents") | |
213 | 228 |
214 table nonces : { Endpoint : string, Nonce : string, Expires : time } | 229 table nonces : { Endpoint : string, Nonce : string, Expires : time } |
215 PRIMARY KEY (Endpoint, Nonce) | 230 PRIMARY KEY (Endpoint, Nonce) |
216 | 231 |
217 fun timeOfNonce s = | 232 fun timeOfNonce s = |
293 return (Some "Signatures don't match") | 308 return (Some "Signatures don't match") |
294 end | 309 end |
295 | Some (left, _) => return (Some ("openid.signed is missing required fields: " ^ show left)) | 310 | Some (left, _) => return (Some ("openid.signed is missing required fields: " ^ show left)) |
296 end | 311 end |
297 | 312 |
298 datatype authentication = AuthenticatedAs of string | Canceled | Failure of string | |
299 | |
300 fun authenticate after r = | 313 fun authenticate after r = |
301 let | 314 let |
302 fun returnTo (qs : option queryString) = | 315 fun returnTo (qs : option queryString) = |
303 case qs of | 316 case qs of |
304 None => after (Failure "Empty query string for OpenID callback") | 317 None => after (Failure "Empty query string for OpenID callback") |
305 | Some qs => | 318 | Some qs => |
306 os <- OpenidFfi.indirect qs; | 319 os <- OpenidFfi.indirect qs; |
307 case OpenidFfi.getOutput os "openid.error" of | 320 case OpenidFfi.getOutput os "openid.error" of |
308 Some v => after (Failure "Authentication failed: {[v]}") | 321 Some v => after (Failure ("Authentication failed: " ^ v)) |
309 | None => | 322 | None => |
310 case OpenidFfi.getOutput os "openid.mode" of | 323 case OpenidFfi.getOutput os "openid.mode" of |
311 None => after (Failure "No openid.mode in response") | 324 None => after (Failure "No openid.mode in response") |
312 | Some mode => | 325 | Some mode => |
313 case mode of | 326 case mode of |
314 "cancel" => after Canceled | 327 "cancel" => after Canceled |
315 | "id_res" => | 328 | "id_res" => |
316 (case OpenidFfi.getOutput os "openid.claimed_id" of | 329 (case OpenidFfi.getOutput os "openid.claimed_id" of |
317 None => after (Failure "Missing identity in OP response") | 330 None => after (Failure "Missing identity in OP response") |
318 | Some id => | 331 | Some id => |
319 errO <- verifyHandle os id; | 332 errO <- verifyReturnTo os; |
320 case errO of | 333 case errO of |
321 HandleError s => after (Failure s) | 334 Some s => after (Failure s) |
322 | HandleOk {Endpoint = ep, Typ = atype, Key = key} => | 335 | None => |
323 errO <- verifyReturnTo os; | 336 errO <- verifyHandle os id; |
324 case errO of | 337 case errO of |
325 Some s => after (Failure s) | 338 HandleError s => after (Failure s) |
326 | None => | 339 | NoAssociation ep => |
340 r <- verifyStateless os ep id; | |
341 after r | |
342 | HandleOk {Endpoint = ep, Typ = atype, Key = key} => | |
327 errO <- verifyNonce os ep; | 343 errO <- verifyNonce os ep; |
328 case errO of | 344 case errO of |
329 Some s => after (Failure s) | 345 Some s => after (Failure s) |
330 | None => | 346 | None => |
331 errO <- verifySig os atype key; | 347 errO <- verifySig os atype key; |
345 in | 361 in |
346 dy <- discover r.Identifier; | 362 dy <- discover r.Identifier; |
347 case dy of | 363 case dy of |
348 None => return "Discovery failed" | 364 None => return "Discovery failed" |
349 | Some dy => | 365 | Some dy => |
350 assoc <- association r.AssociationType r.AssociationSessionType dy; | 366 case r.Association of |
351 case assoc of | 367 Stateless => |
352 AssError msg => return ("Association failure: " ^ msg) | |
353 | AssAlternate _ => return "Association failure: server didn't accept its own alternate association modes" | |
354 | Association assoc => | |
355 redirect (bless (dy ^ "?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=" | 368 redirect (bless (dy ^ "?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=" |
356 ^ r.Identifier ^ "&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.assoc_handle=" | 369 ^ r.Identifier ^ "&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=" |
357 ^ assoc.Handle ^ "&openid.return_to=" ^ show (effectfulUrl returnTo))) | 370 ^ show (effectfulUrl returnTo))) |
371 | Stateful ar => | |
372 assoc <- association ar.AssociationType ar.AssociationSessionType dy; | |
373 case assoc of | |
374 AssError msg => return ("Association failure: " ^ msg) | |
375 | AssAlternate _ => return "Association failure: server didn't accept its own alternate association modes" | |
376 | Association assoc => | |
377 redirect (bless (dy ^ "?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=" | |
378 ^ r.Identifier ^ "&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.assoc_handle=" | |
379 ^ assoc.Handle ^ "&openid.return_to=" ^ show (effectfulUrl returnTo))) | |
358 end | 380 end |
359 | 381 |
360 task periodic 1 = fn () => | 382 task periodic 1 = fn () => |
361 dml (DELETE FROM discoveries WHERE Expires < CURRENT_TIMESTAMP); | 383 dml (DELETE FROM discoveries WHERE Expires < CURRENT_TIMESTAMP); |
362 dml (DELETE FROM associations WHERE Expires < CURRENT_TIMESTAMP); | 384 dml (DELETE FROM associations WHERE Expires < CURRENT_TIMESTAMP); |