Skip to content

Passwordless Login Sending Login Mail Websharper

This post is part of series starting here.

Sending the mail

Sending the mail is not hard, but will show how code running at the client side can call RPC functions provided by the server. These calls are totally transparent.

For the login page, we add an endpoint:

    | [<EndPoint "/l">] Login

and map it to a function all as seen earlier:

        Application.MultiPage (fun ctx endpoint ->
            match endpoint with
            | EndPoint.Home -> HomePage ctx
            | EndPoint.About -> AboutPage ctx
            | EndPoint.Admin token ->
                if token<> "" then
                    AuthPage ctx token
                else
                    AdminPage ctx
            | EndPoint.Login -> LoginPage ctx

That function is not complex, but uses clientside code to display the form:

        let body = div [] [
                       div [] [t.tt("If your email address was added as an admin, enter it below and submit the form to get login instructions by mail.")]
                       div [] [client <@ Client.LoginForm () @>]
                   ]

        Templating.Main ctx EndPoint.About (t.t("Login")) [
            h1 [] [t.tt "Login"]
            body
            ]

The client helper function lets you inject, from the server, code running at the client side.

Here’s the Client.LoginForm function is using this template to render the form

    <div ws-template="LoginForm">
        <input ws-var="Email" />
        <button type="button" class="btn btn-primary" ws-onclick="OnSubmit">${Login}</button>
        <div>${Info}</div>
    </div>

and the function itself is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    let LoginForm () =
        let info = Var.Create ""
        Templates.MainTemplate.LoginForm()
            .Login("Submit")
            .OnSubmit(fun e ->
                Server.SendLoginMail(e.Vars.Email.Value) |> Async.StartImmediate
                e.Vars.Email.Set ""
                info.Set ("If your email is registered as admin you will reveive a mail with login information")
            )
            .Info(info.View)
            .Doc()

It starts by creating a Var with the value "" (line 2). A Var’s value can be updated by calling the .Set method (line 8), and its corresponding view will be automatically updated. This is particularly useful when injecting a view in an HTML template (line 10). On line 3 the form’s template is instanciated, and values are injected for its placeholders. Eg, the template’s ${Login} is replaced by Submit. In this example this might have been hard-coded in the template, but in reality I’m using a localisation function to inject the string in the correct language. The template’s button has the attribute ws-onclick="OnSubmit", which binds its click event to the function injected with the template’sOnSubmit method (line 5). What is done at submit of the form is call the RPC SendLoginMail on the server, passing the value of the Var Email defined in the template with <input ws-var="Email" />. The value of the Var Email is set to the empty strings, which also empties the form’s field; and the value of the info Var is set. As the view of this Var is injected in the template, the value displayed will immediately be updated to reflect the Var’s new value.

The server’s function is basically just sending the mail with mailer.Send(email,subject, body), the implementation of which is covered in another post.