Skip to main content

Authentication

Patterns for handling user authentication in SolidOS.

Basic Login/Logout

Check Login Status

import { authn } from 'solid-logic'

function checkAuth() {
const user = authn.currentUser()

if (user) {
console.log(`Logged in as: ${user.uri}`)
return user
} else {
console.log('Not logged in')
return null
}
}

Login

import { authn } from 'solid-logic'

async function login() {
try {
await authn.login()
const user = authn.currentUser()
console.log(`Logged in as: ${user.uri}`)
} catch (error) {
console.error('Login failed:', error)
}
}

Login with Specific Provider

async function loginWithProvider(providerUrl) {
await authn.login({
provider: providerUrl
})
}

// Usage
await loginWithProvider('https://solidcommunity.net')
await loginWithProvider('https://login.inrupt.com')

Logout

async function logout() {
await authn.logout()
console.log('Logged out')
}

Session Events

Listen for Login

import { authn } from 'solid-logic'

authn.onLogin((webId) => {
console.log(`User logged in: ${webId}`)

// Update UI
showLoggedInUI(webId)

// Load user data
loadUserProfile(webId)
})

Listen for Logout

authn.onLogout(() => {
console.log('User logged out')

// Update UI
showLoggedOutUI()

// Clear cached data
clearUserData()
})

Protected Operations

Require Login Before Action

async function requireAuth() {
const user = authn.currentUser()
if (!user) {
await authn.login()
}
return authn.currentUser()
}

async function saveNote(content) {
const user = await requireAuth()
if (!user) {
throw new Error('Login required')
}

// Proceed with save
await createNote(user, content)
}

Login Gate Component

function createAuthGate(dom, onAuthenticated) {
const container = dom.createElement('div')

const user = authn.currentUser()
if (user) {
onAuthenticated(container, user)
} else {
const message = dom.createElement('p')
message.textContent = 'Please log in to continue'
container.appendChild(message)

const loginBtn = UI.widgets.button(dom, 'Log In', async () => {
await authn.login()
const newUser = authn.currentUser()
if (newUser) {
container.innerHTML = ''
onAuthenticated(container, newUser)
}
})
container.appendChild(loginBtn)
}

return container
}

// Usage
const gate = createAuthGate(document, (container, user) => {
container.textContent = `Welcome, ${user.uri}!`
})

User Profile

Load Profile Data

import { store, sym } from 'solid-logic'

const FOAF = Namespace('http://xmlns.com/foaf/0.1/')
const VCARD = Namespace('http://www.w3.org/2006/vcard/ns#')

async function loadProfile(webId) {
const person = sym(webId)
await store.fetcher.load(person.doc())

return {
webId: webId,
name: store.any(person, FOAF('name'), null, null)?.value ||
store.any(person, VCARD('fn'), null, null)?.value,
photo: store.any(person, FOAF('img'), null, null)?.uri,
email: store.any(person, FOAF('mbox'), null, null)?.uri?.replace('mailto:', ''),
}
}

Get Pod Root

import { profile } from 'solid-logic'

async function getPodRoot(webId) {
const podRoot = await profile.getPodRoot(webId)
return podRoot // e.g., 'https://alice.example/'
}

Get Inbox

const LDP = Namespace('http://www.w3.org/ns/ldp#')

async function getInbox(webId) {
const person = sym(webId)
await store.fetcher.load(person.doc())

const inbox = store.any(person, LDP('inbox'), null, null)
return inbox?.uri
}

Session Persistence

Check Session on Page Load

document.addEventListener('DOMContentLoaded', async () => {
// Check if there's a valid session
const user = authn.currentUser()

if (user) {
// Session exists, update UI
updateUIForUser(user)
} else {
// No session, show login UI
showLoginUI()
}
})

Handle Session Expiry

// Session can expire, handle gracefully
async function fetchWithAuth(uri) {
try {
await store.fetcher.load(uri)
} catch (error) {
if (error.status === 401) {
// Session may have expired
const user = authn.currentUser()
if (!user) {
// Need to re-login
await authn.login()
// Retry the request
await store.fetcher.load(uri)
}
} else {
throw error
}
}
}

Multiple Users

Display Current User

function createUserBadge(dom) {
const container = dom.createElement('div')
container.className = 'user-badge'

const user = authn.currentUser()
if (user) {
const avatar = UI.widgets.avatar(dom, user, { size: 32 })
container.appendChild(avatar)

loadProfile(user.uri).then(profile => {
const name = dom.createElement('span')
name.textContent = profile.name || 'User'
container.appendChild(name)
})
}

return container
}

Switch Accounts

async function switchAccount() {
// Logout current user
await authn.logout()

// Login as different user
await authn.login()
}

Security Considerations

Never Store Credentials

// DON'T do this:
// localStorage.setItem('password', password)

// DO use Solid-OIDC through authn
await authn.login()

Validate User Input

function isValidWebId(uri) {
try {
const url = new URL(uri)
return url.protocol === 'https:'
} catch {
return false
}
}

See Also