Skip to content

Passwordless Login With Websharper

Intro

I had the need recently to provide some admin access to a web application, but adding password management in that application was not worth the trouble. It was much easier to allow users to request a authentication link by email, as this post will show. The web app is dveloped in WebSharper. If you’re a F# developed, you should really check it out! I like that I can develop client and server in one integrated code base, all in F#. Its list of good things is too long to enumerate here, so do yourself a favor and check it out! This post is not a WebSharper tutorial though, if you need one I recomment Alex Peret’s tutorial.

The app I’m covering here is backed by an sqlite database, with admin users listed in the table admins, which has only two columns: email and token. When admin users want to login, they request the authentication link, which includes their token, to be sent to their email. Clicking on the link will authenticate them, create a session and mark them as logged in, and display the admin page, only available to logged in users.

Admin users are created manually in the database, there’s no registration available, and tokens are random strings. You might want to use more advanced tokens.

Implementation

My authentication path is /a/${token} and when a token is present, the authentication occurs. When the token is not present, the admin page is displayed if the user accessing it is logged in. To achieve this I define the corresponding endpoint:

[<EndPoint "/a">] Admin of token:string

If no token is present in the path, the token value will be the empty string, which is not a valid token ever assigned to a user. That way we can distinguish both cases. The distinction of both cases it visibile in the code:

        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

The AuthPage will simply validate the token. If it is valid, it registers the user as logged in and redirects it to the admin page, otherwise it redirects to the login page.

        if validateToken token language then
            ctx.UserSession.LoginUser (System.DateTime.Now.ToString()) |> Async.StartImmediate
            Content.RedirectTemporary (EndPoint.Admin "")
        else
            Content.RedirectTemporary EndPoint.Login

Validating the token is simply checking there is a user with the token.

The Admin page itself will check the user accessing it is logged in before displaying its content, and redirect to the login page otherwise:

        async {
                    let! user = ctx.UserSession.GetLoggedInUser()
                    if user.IsSome then
                        let body = ...
                        return! Templating.Main ctx EndPoint.About (t.t("Admin")) [
                            h1 [] [t.tt "Admin"]
                            p [] [t.tt "Welcome"]
                            body
                        ]
                    else
                        return! Content.RedirectTemporary EndPoint.Login
        }

Note: the t.tt call is for internationalisation, and will translate the text in the user’s language.

And this is it, we have the authentication done. Sending the authentication link will be shown in a subsequent post, and involves more WebSharper features like client side code, RPC calls, reactive variables. That alone warrants to make it a dedicated post!