Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time. Cannot retrieve contributors at this time
132 lines (94 sloc) 4.63 KB

Simple user sessions

This is a proposal to provide APIs and UIs for creating sessions with user identities built on Dat.

In the existing Beaker Browser toolset, developers can prompt the user to select a Dat archive using DatArchive.selectArchive. That API could be adapted to act as a kind of login experience by prompting the user to select an archive that should represent their profile. However, we wish to provide users with a clear set of actions and interfaces around user identities. In particular, we think it is important to:

  • Represent "user dats" as identities
  • Protect access to user dats
  • Indicate in Beaker's core UIs which user dat the application is actively using
  • Provide tools for the user to change which user dat is live
  • Provide a consistent developer experience

In this document, we'll propose a concise set of concepts, APIs, and UIs for user dats and user dat sessions.

Concepts

User dats

User dats are Dat archives with the 'user' type (see 0005-dat-archive-types).

User dats contain all of the content and data which should be attached directly to the user identity. The browser may manage many user dats, and users can freely switch between them within an application (therefore changing the active session).

When viewed from the browser's builtin interfaces, a user dat is represented as a user identity. This may include special icons, labels, and categorizations.

User dat sessions

User dat sessions are created by the user completing the signin flow. Each session has an ID, which is the URL of the user dat. An application can request a session using navigator.session.request().

Session lifetime

The session has no expiration time, but the user can end a session using the browser UI. An application can also end its session with navigator.session.end().

Permissions

Applications with an active session may read and write any non-protected file on the user dat. After the session ends, all write permission is revoked.

As with any other Dat archive, an application may request permission to write to a user dat without a user session. The browser should present a special warning and confirmation message within the write-access permission prompt, e.g., "This application is asking for permission to edit your user profile. Are you sure you want to continue?"

APIs

To access the signin and session flow for user dats, we propose the navigator.session API.

navigator.session.id // => the url of the session's dat, or false if no session exists
await navigator.session.request()
await navigator.session.end()

User interfaces

URL bar Indicator UI

The URL bar should display an Indicator UI for the active session. This might exist as a small icon and/or label. Clicking on the Indicator should open the Session Viewer.

Signin UI

The Signin is a popover or modal. It is triggered by a click on the Indicator, or by the navigator.session.request() API. It includes:

  • A selectable list of available users
  • A control to create a new user

Session Viewer UI

The Session Viewer is a popover. It is triggered by a click on the Indicator. It includes:

  • Information about the active session (User name, avatar)
  • Links to view the session's user profile in more detail
  • Controls to logout and change the active session

Example code

Here is a simple example usage of the session API:

var profile // user's profile data
setup()

async function setup () {
  // if we already have an active session, fetch profile data 
  if (navigator.session.id) {
    await getUserData()
  }
  updateUI()
}

async function getUserData () {
  var archive = new DatArchive(navigator.session.id)
  profile = JSON.parse(await archive.readFile('/profile.json'))
}

function updateUI() {
  // main render function
  // we call this function any time the state changes
  yo.update(document.querySelector('main'), yo`<main>
    <header>
      ${renderProfile()}
      ${renderLogin()}
    </header>
    <main>
      ...
    </main>
  </main>`)
}

function renderLogin () {
  // the UI for 'Log in' / 'Change profile'
  return yo`<div class="login">
    <button onclick=${doLogin}>${navigator.session.id ? 'Change profile' : 'Log in'}</button>
  </div>`
}

function renderProfile () {
  if (!profile) {
    return ''
  }
  return yo`<div class="profile">
    <img src=${profile.avatar}>
    <span>${profile.name}</span>
  </div>`
}

async function doLogin () {
  try {
    await navigator.session.request()
    await getUserData()
  } catch (e) {
    // failed, probably because the user cancelled
  }
  updateUI()
}