Summary
- Yes, it is possible to do with ports
- The ports design is a little awkward (could have been a lot awkward if uses cases were not so similar)
- I’m curious if there are better solutions for “call synchronous function and use the result in different ways”
Problem Description
I wanted to do a lightweight bespoke authentication system for a small app; essentially I had an admin panel where I wanted to authenticate that I was the only one who could send admin commands, plus a secondary client where I had the only instance, and could enter the private key. I was aware of libsodium and the libsodium Javascript transpilation from following SQRL
Native Code
With the possible exception of key generation, cryptography functions are pretty functional - data in, data out, synchronous operation. I wanted to call a series of functions without roundtripping and then figuring out what to do with the result. I did not consider rewriting in Elm a responsible option - cryptography is really hard to get right, and even timing can be attacked, whereas Elm is still undergoing major changes in language and optimization. However, with rumors of 0.19, I figured I should look at just how ports could work.
Ports Conversion
The (very basic) authentication method is to sign the request body with a private key. (Yes, I know there is a lot of room for improvement here, this is not the part I’m looking for suggestions on.)
This used to be done synchronously in “send a request” functions which returned a Cmd. In order to convert to ports, I would need to send the information to a port, and then somehow correlate the response to the HTTP request I wanted to send. Ports don’t take tagging functions in the same way that HTTP requests do, so there is no way to attach a Msg with some data. In order to store some correlation data in the model, I would have to pervert the organization of the application away from “call a function, get a Cmd”, and would not be able to use a general continuation style mechanism without breaking the rule against storing functions in the model.
Fortunately, all of my HTTP calls are currently similar enough to use the same Msg tagger, so I was able to send the key information (url, method, etc) to the port; the result is still a Cmd. The corresponding response sends the information back with the signed body, and the port handler can always make the same HTTP call out of it. If the calls were less similar, I would have to include some sort of response identifier in the payload, or even perform the HTTP calls on the JS side, foregoing all of Elm’s protections in that area.