Static Website Authentication with Magic Link Made Easy

Following up on A Login Security Architecture Without Passwords I recently had the opportunity to try out how easy it is to actually implement the "no password" login architecture. The problem to solve was publishing a small static website (an online conference program) to be accessible by several hundred people, of whom we only have the email address (that they used to sign up for the event). The event is private and the program should therefore also not be published to the open Internet, but stay private. In this example the conference program was a custom built website published via static website hosting.

The code and more information on how to get started is published at github.com/schlomo/static-website-with-magic-link-auth

Problem Analysis

Magic link authentication (get a link via email to log in instead of bothering with a password) is in my humble and honest opinion the best — if not only — solution for this problem (see also my talk 🎦 Lifting the Curse of Static Credentials:

  • it relies on the available information — the email used to sign up for the event
  • it doesn't make any assumptions on the users' environment or preferences — works on any device
  • it doesn't collect more information than strictly required (no phone, first / last name or other personal information)

In a nutshell, the process looks like this (click for more details):

To make our implementation even simpler, there are additional factors we can take into account. Specifically the conference program should not be public on the Internet, but it is OK for users to share it privately:

  • the security requirements of the conference program are moderate, so that a simple one-time verification of the participants is sufficient. Combined with a long lasting session, this means that users need to "sign in" only once per device.
  • we can accept the risk of users forwarding the magic link (this is important as users could use a different email between signing up to the conference on their PC and accessing the conference program on their phone) or accessing it from a different devices (e.g. sign in on PC and phone with the same link).
  • we can accept the risk of session hijacking and other attacks happening on the users' side (this is not a banking app 🤪)

Initially I wanted to find a ready made solution for this, but sadly couldn't find any. So a bit of LLM-supported coding later I'm happy to present simple static website hosting solution with built-in magic link authentication:

  • Based on NodeJS Express to serve static content and handle the magic link authentication
  • Using standard libraries like nodemailer to send out email
  • Data privacy friendly, using only email for login and not even storing that in the configuration or the session info
  • Operations friendly, no database or persistent storage used (accepting the security / risk trade-offs mentioned above)
  • Deployment friendly, simply replace your existing static website Docker image with this one to add authentication

Other solutions to consider would be facilitating a CDN with dynamic authentication handling, like Cloudflare Workers. However, for this case I chose to develop a solution that doesn't depend on a specific hosting environment or Cloud service.

Live Demo

Implementation Details

To keep the implementation very simple (the entire solution fits into less than 1000 lines of code including comments) and data privacy friendly, we use some tricks:

The ongoing authentication verification is based on a cryptographically signed session. The session is stored in a cookie that is not accessible via JavaScript. The session only contains {"authenticated": true} as information, we don't care about the user identity for this simple use case. The actual security is based in the signature of the session, that prevents manipulations by a user. The signature is created with the help of a static secret known only to the server. For the given use case I think that this is "good enough".

The login is handled via a token that is encrypted and emailed to the user upon request. The token only contains {"expires":1746433589349, "redirect": "/about.html"} which is the validity of the login link (typically 15 minutes) and the path to go to after signing in. Again we don't care about the user identity after checking it in our list of allowed emails. The login link takes the a function that verifies the token, sets the session to be authenticated and redirects the browser to the actual static website content. At this stage we already don't know the identity of the user as we don't keep that information anywhere.

The list of allowed emails is currently provided via a simple text file as part of the application. This text file accepts both plain email addresses and also SHA-256 hashes. All emails are hashed on import and stored only as hashes.

Configuration & Deployment

Configuration happens via environment variables, see server.js for a full list of supported variables and their defaults and the project README.md for more information.

You can deploy it to any Javascript hosting service, use my Docker image directly, or base your own Docker image on it.

For a quick test run the Git repository contains an example.env configuration that works for sending emails to Google accounts without requiring an email sending service.

Lessons Learned

Moving away from passwords is much easier than I feared myself — and it can significantly increase the security posture and reduce the data privacy risks. Even if my server would get hacked there is literally nothing on it that is secret, especially if the email addresses are already hashed in the configuration.

Most of the time implementing this was spent on polishing the code and solving edge cases, while the actual magic link solution was very easy to build. I hope that this little example will help you to protect your own static website with a simple login mechanism. And maybe it can motivate you to get rid of passwords in other places, too.

Interested in how you can get rid of passwords in your environment? Please reach out to us, Tektit Consulting and I are happy to support you in getting rid of passwords, increasing the user experience and improving your securite posture.

Comments

Like this content? You could send me something from my Amazon Wishlist. Need commercial support? Contact me for Consulting Services.

Popular posts from this blog

Overriding / Patching Linux System Serial Number

Fixing Chrome Color Printing on Linux with HP Color LaserJet M880

Bitkom Forum Open Source 2024 in Erfurt