Last week we explored the concept of a service that lets you tweet anonymously while using the lightning network to prevent spam. I’ve started building a minimal prototype of the website. So let’s go over my development process, the issues I encountered, and what’s next.

LNURL-Auth

The first thing I tackled was implementing LNURL-Auth so that users can login with their lightning wallet. There needed to be some sort of account and login method that lets users withdraw only the sats that they’ve put up. I decided on LNURL-Auth for several reasons:

  1. It allows users to simply login without having to go through an account creation process
  2. It is password-less and theoretically more secure than a traditional username and password
  3. No personal information other than the wallet’s public keys and signature are required

User Flow

Here’s what the front page looks like:

https://imgur.com/cW6GFXi.png

I know its really flashy, but I wanted to draw attention to the site! Jokes aside, I’ll make the user interface more user friendly and attractive once all the functional features are in place.

When you click “login” it brings you to a page with a qr code.

https://imgur.com/DqRWVgj.png

Scanning the qr code with an LNURL-Auth enabled wallet will allow you to login to the site. You are automatically redirected to the front page.

If two minutes passes without the qr code being used to login, you will be asked to generate a new qr code.

Under the Hood

I implemented this login system following fiatjaf’s LNURL-Auth spec. I had hoped to find a Django or python module that had already implemented it, but had no luck. So I decided to create it from scratch.

When you get to the login page, the view for the page creates a random 32 byte value called k1 using the secrets module. I then cache a key-value pair with k1 as the key and an empty string as the value for two minutes. After two minutes, it is automatically deleted from the server’s cache.

def login(request):
    k1 = secrets.token_hex(32) # Generating the random value

    cache.get_or_set(k1, '', 120)

    url = f"{config['DEBUG']['BaseUrl']}/lnlogin/auth?tag=login&k1={k1}"
    lnurl = encode(url)
    context = {
        'lnurl': lnurl,
        'challenge': k1
    }
    return render(request, 'lnlogin/login.html', context)