adam@0: task initialize = fn () => OpenidFfi.init adam@1: adam@2: fun discover s = adam@2: r <- OpenidFfi.discover s; adam@2: return (Option.mp (fn r => {Endpoint = OpenidFfi.endpoint r, adam@2: LocalId = OpenidFfi.localId r}) r) adam@3: adam@3: val createInputs = adam@3: is <- OpenidFfi.createInputs; adam@3: OpenidFfi.addInput is "openid.ns" "http://specs.openid.net/auth/2.0"; adam@3: return is adam@3: adam@3: table associations : { Endpoint : string, Secret : string, Expires : time } adam@3: PRIMARY KEY Endpoint adam@3: adam@3: task periodic 0 = fn () => dml (DELETE FROM associations WHERE Expires < CURRENT_TIMESTAMP) adam@3: adam@3: datatype association = Handle of string | Error of string adam@3: adam@3: fun association url = adam@3: secret <- oneOrNoRowsE1 (SELECT (associations.Secret) adam@3: FROM associations adam@3: WHERE associations.Endpoint = {[url]}); adam@3: case secret of adam@3: Some v => return (Handle v) adam@3: | None => adam@3: is <- createInputs; adam@3: OpenidFfi.addInput is "openid.mode" "associate"; adam@3: OpenidFfi.addInput is "openid.assoc_type" "HMAC-SHA256"; adam@3: OpenidFfi.addInput is "openid.session_type" "no-encryption"; adam@3: os <- OpenidFfi.indirect url is; adam@3: case OpenidFfi.getOutput os "error" of adam@3: Some v => return (Error v) adam@3: | None => adam@3: case (OpenidFfi.getOutput os "assoc_handle", OpenidFfi.getOutput os "expires_in") of adam@3: (Some handle, Some expires) => adam@3: (case read expires of adam@3: None => return (Error "Invalid 'expires_in' field") adam@3: | Some expires => adam@3: tm <- now; adam@3: dml (INSERT INTO associations (Endpoint, Secret, Expires) adam@3: VALUES ({[url]}, {[handle]}, {[addSeconds tm expires]})); adam@3: return (Handle handle)) adam@3: | _ => return (Error "Missing fields in response from OP")