Pod Mapping with Solid: Setup and Authentication

podmap.me is a Web-based implementation of Mia Mingus and the Bay Area Transformative Justice Collective's Pod Mapping Worksheet. Pod data is incredibly sensitive and personal - in the wrong hands, detailed information about the relationships we have with the most important people in our lives can be used to exact enormous harm. It is unacceptable, therefore, to let this data escape a podmap.me user's locus of control, and unethical to build a Web service that asks a user to do this.

Until now we've solved this problem by storing all data in the browser's local storage. This is certainly an improvement in some ways over the paper worksheet, but local browser storage is still too ephemeral and opaque to be useful for long-term storage of this data, tying the user to whatever device they happen to have used to complete the worksheet. It would be much better to store this data in a place fully controlled by the user that is also accessible by the various devices and apps they use: a place like their personal online datastore.

This series of articles will be a journal of the process of updating podmap.me to store data in the personal online datastores of its users. We'll be starting from the "Local Storage" release of podmap.me. You can follow along on the solid branch of the Git repository.

Setup

podmap.me is built with Next.js and React so swrlit is a perfect choice for data and state management:

npm install swrlit

swrlit exports an AuthenticationProvider that will store and manage authentication state for our app. We'll add it to _app.jsx to ensure it's available on every page:

import { AuthenticationProvider } from 'swrlit'

function MyApp({ Component, pageProps }) {
  return (
    <AuthenticationProvider>
      <Component {...pageProps} />
    </AuthenticationProvider>
  )
}

You can see the full commit here.

Authentication

We can't start storing data until we can log in, so the first thing we'll do is to implement authentication. To keep things simple we'll add a simple log in form to the navigation bar:

import { useState } from 'react'
import Link from 'next/link'

import { useAuthentication } from 'swrlit'

export default function Nav() {
  const [handle, setHandle] = useState("")
  const [badHandle, setBadHandle] = useState(false)
  const { loginHandle } = useAuthentication()
  async function logIn(){
    setBadHandle(false)
    try {
      await loginHandle(handle);
    } catch (e) {
      console.log("error:", e)
      setBadHandle(true)
    }
  }
  function onChange(e){
    setHandle(e.target.value)
    setBadHandle(false)
  }
  return (
    <nav>
      <ul>
        <li>
          <input type="text"
                 placeholder="what's your handle?"
                 value={handle} onChange={onChange} />
          {badHandle && (
            <p className="text-xs text-red-500 absolute">
              hm, I don't recognize that handle
            </p>
          )}
        </li>
        <li>
          <button onClick={logIn}>log in</button>
        </li>
      </ul>
    </nav>
  )
}

You can see the finished version, with styling and convenience functions, here.

swrlit's loginHandle function uses a couple heuristics to construct the "identity provider" URL, so when I enter tvachon.inrupt.net in the "what's your handle?" input I'll be redirected to the Solid Pod hosting service at inrupt.net to enter my credentials before being redirected back to podmap.me:

podmap.me login form in nav bar inrupt.net login form

Now that we're back, we have two problems - the login form is still showing, and we have no way to log out. Fortunately this couldn't be easier to fix:

import { useAuthentication, useLoggedIn } from 'swrlit'
export default function Nav() {
  const { loginHandle, logout } = useAuthentication()
  const loggedIn = useLoggedIn()
  ...
  return (
    ...
        {loggedIn ? (
           <li>
             <button onClick={logout}>log out</button>
           </li>
         ) : (
           <>
             <li>
               <input type="text"
                      placeholder="what's your handle?"
                      value={handle} onChange={onChange} />
               {badHandle && (
                 <p className="text-xs text-red-500 absolute">
                   hm, I don't recognize that handle
                 </p>
               )}
             </li>
             <li>
               <button onClick={logIn}>log in</button>
             </li>
           </>
         )}
    ...
  )
}

You can see the full commit here.

Next time

In the next article in this series we'll show you how to start creating, saving and updating data in our personal online datastore using swrlit.

If you find this tutorial useful, please consider donating to the Bay Area Transformative Justice Collective, whose work inspired podmap.me.