aantron/dream

Why does `verify_csrf_token` return a promise?

Closed this issue · 1 comments

The entire body of verify_csrf_token is just wrapped in Lwt.return, making me think that it could be made synchronous (and so somewhat less awkward to use).

dream/src/server/csrf.ml

Lines 40 to 82 in d418a79

let verify_csrf_token ~now request token = Lwt.return @@
match Dream_pure.Formats.from_base64url token with
| None ->
log.warning (fun log -> log ~request "CSRF token not Base64-encoded");
`Invalid
| Some token ->
match Cipher.decrypt ~associated_data:field_name request token with
| None ->
log.warning (fun log -> log ~request "CSRF token could not be verified");
`Invalid
| Some token ->
(* TODO Don't raise exceptions. *)
match Yojson.Basic.from_string token with
| `Assoc [
"session", `String token_session_label;
"expires_at", (`Float _ | `Int _ as expires_at);
] ->
let expires_at =
match expires_at with
| `Float n -> n
| `Int n -> float_of_int n
in
let real_session_label = Session.session_label request in
if token_session_label <> real_session_label then begin
log.warning (fun log -> log ~request
"CSRF token not for this session");
`Wrong_session
end
else
let now = now () in
if expires_at > now then
`Ok
else begin
log.warning (fun log -> log ~request "CSRF token expired");
`Expired expires_at
end
| _ | exception _ ->
log.warning (fun log -> log ~request "CSRF token payload invalid");

It is for future-proofing, because a different CSRF token verification technique (for example, checking a cache or a database) might need I/O. If I/we become fully convinced that signed CSRF tokens are "always" good enough (at least as a default), we can drop the promise from the signature. There might be other arguments for dropping it. Also, this might become less relevant once Dream is ported to effects, though I am not yet sure how the API will look.