Best practice for reading secrets from form inputs

I am new to Elm (Udemy course last weekend and now going through elmprogramming.com) but have used C++, Python, Java, Javascript for a long time. My single page app is making REST api calls and I would like to use an input of type password to store the API auth token.
The goal is to read the API token directly from the input when a ‘Submit’ button is hit. The motivation behind not wanting to store the API token in a model field is better security, or so I thought, by containing it only in the html form input and nowhere else. It was not immediately clear how to do that. Then I found this 2018 issue, and I am interested in knowing the details of how to use the previousSibling method to read the API token. I understand it’s not kosher, but this is just a tool I plan on locally using, not going to be a production grade app.
This is where I saw the metion
By the way, would javascript interops be a more preferrable way to do it?

1 Like

Here is some sample code where on a click of a button, the custom decoder reads the value of a sibling input

The thing is that it makes no sense to me why would you want to do that.

An API auth token should be stored in the Model and used in the update. It has no reason to end up in a view element.

3 Likes

Thanks for the sample code.

Regarding the why, the paranoia is due to javascript security articles like this and other similar ones, it’s not that I think this will happen in this situation, just trying to avoid vars for secrets, if possible - since I work for a secret key management group. But pretty much all my code is in the server side, and I am not so cognizant of dangers lurking on the client side. Maybe once I have the time to study the compiled javascript code, I will know better.

I was also in a rush to get this working and the online courses, as well as the elm guide show that for http requests, the Union Type is used for the model

type Model
  = Failure
  | Loading
  | Success String

whereas the other examples used a type alias for the model, and I was not sure how to combine those

type alias Model
  = { secret : String, otherStuff : SomeType }

After some tinkering around, I have an inkling that this might work

type Model
  = Failure
  | Loading
  | Success (List result)  {secret: String, otherStuff : SomeType}

where result is a type alias I have defined as the Json list that is returned from my API query

type alias result = { number : Int, html_url : String, title : String }

Am I on the right track, that the Union Types can have arbitrary parameters (not sure I am using the right term) and one of them could be all the data of interest that I need to keep track of?

Having a union type with payloads for the different states is definitely a nice way to go, the Remotedata package is a common choice for modeling it (check out How Elm Slays a UI Antipattern)

On the original question of security, if you control both the front and backend I would look at using cookie based auth, if you set it as a Http only cookie it will be inaccessible from JS. With the password input someone could still read the API key from the input with JS, like document.querySelector("input[type=password]").value

1 Like

How would this code change if there were 2 different token inputs? Would it be map2 instead of map? Not sure how that would look in the pipeline format.

Yeah, you would use map2


onSubmit : (( String, String ) -> msg) -> Attribute msg
onSubmit toMsg =
    Json.succeed Tuple.pair
        (Json.at [ "target", "previousSibling", "previousSibling", "value" ] Json.string)
        (Json.at [ "target", "previousSibling", "value" ] Json.string)
        |> Json.map toMsg
        |> on "click"

or, if you declare andMap (or import it from Json.Decode.Extra)

andMap : Decoder a -> Decoder (a -> b) -> Decoder b
andMap =
    Json.map2 (|>)


onSubmit : (( String, String ) -> msg) -> Attribute msg
onSubmit toMsg =
    Json.succeed Tuple.pair
        |> andMap (Json.at [ "target", "previousSibling", "previousSibling", "value" ] Json.string)
        |> andMap (Json.at [ "target", "previousSibling", "value" ] Json.string)
        |> Json.map toMsg
        |> on "click"