haz
haz

Reputation: 2308

Idiomatic authentication in Elm

I'm trying to wrap my head around Elm. I have experience in Haskell, and a bit of Erlang.

I want to complete the following exercise:

  1. User is shown a login form
  2. On submit, the frontend makes a request to localhost/auth to try and receive an auth token.
  3. On success, the homepage is shown, which fetches some data.
  4. On failure, the login screen displays an error.

This is quite basic, but hopefully complex enough to model the behaviour of a real webapp.

My first problem is with the Model. I only need the data if the client is authenticated. Should I wrap this in something similar to a Maybe monad?

type Model
    = NoAuth String String
    | AuthFetching
    | AuthFailed err
    | AuthSuccess String

And then the login screen can display a spinner, and error, or redirect to a new page.

This feels like it ties the rest of the applications state to the authentication state. Although is is "correct", it feels wrong to have the whole model be a (variant type?) with only one record.

It "feels" more correct to have the model like so:

type FetchStatus
    = Loading
    | Success val
    | Err err

type Model =
    { token : RequestStatus String
    , data  : List number
    }

But whenever you update the model, you now need to check if token is still present - i.e. pattern match within the record. In the first example, you only needed to pattern match on the whole model, which is a bit simpler.

And to hold the login form state, I'd need to add extra fields:

type Model =
    { token : RequestStatus String
    , data  : List number
    , username : String
    , password : String
    }

Which feels incorrect because the password should not be held in memory after login. I'd hold these in records, but I cannot use records in custom type declarations.

All in all, I'm a bit confused. Can someone please shed some light on the most "correct", idiomatic way to do this?

Upvotes: 3

Views: 412

Answers (1)

radrow
radrow

Reputation: 7159

All authorization related stuff should be handled on the backend side. Elm's job is only to display what server has sent to it. In my opinion the first option you proposed is the best for such a little example, but in more real-life application the typesystem would be more complex:

type LoginForm =
  { username : String
  , password : String
  }

type Activity
  = Login LoginForm
  | LoginSuccess
  | LoginFailure String  

type Model =
  { loggedUser : Maybe String
  , activity : Activity
  , ...
  }

You don't need (and shouldn't) keep password on frontend. You also shouldn't perform any authorizations on the client side, as the client may easily replace any script in his browser. The backend will track whether the user is logged in by eg. session cookies. In this scenario even if the loggedUser value is set to Just "someguy" and "someguy" is not marked as logged in the server database, any action that requires authorization shall fail.

Summarizing, handling login and giving permissions to access any content is a job for backend. Elm is frontend language, so it's only purpose here is to display things.

Upvotes: 4

Related Questions