Skip to content

Authentication

HumanWallet's authentication system is built on WebAuthn (passkeys), providing secure, passwordless authentication. The core package includes several functions for managing user authentication lifecycle.

Functions Overview

register()

Creates a new passkey-based account for a user.

Function Signature

function register(
  username: string,
  config: Config,
): Promise<{ sessionKeyAccount: SessionKeyAccount; kernelClient: KernelClient }>

Parameters

  • username: Unique identifier for the user (used for passkey creation)
  • config: Configuration object from createConfig()

Usage Example

import { createConfig, register } from "@humanwallet/core"
import { sepolia } from "viem/chains"
 
const config = createConfig({
  projectId: "your-project-id",
  chain: sepolia,
})
 
try {
  const { sessionKeyAccount, kernelClient } = await register("alice", config)
  console.log("Account created:", sessionKeyAccount.address)
 
  // User is now authenticated and ready to interact
} catch (error) {
  if (error.message.includes("User rejected")) {
    console.log("User cancelled passkey creation")
  } else {
    console.error("Registration failed:", error)
  }
}

What Happens During Registration

  1. Passkey Creation: Browser prompts user to create a passkey using biometrics/PIN
  2. Account Generation: A smart contract wallet is created using the passkey
  3. Session Key: A temporary session key is generated for seamless interactions
  4. Storage: Authentication data is securely stored locally

login()

Authenticates an existing user with their passkey.

Function Signature

function login(
  config: Config,
): Promise<{ sessionKeyAccount: SessionKeyAccount; kernelClient: KernelClient }>

Parameters

  • config: Configuration object from createConfig()

Usage Example

try {
  const { sessionKeyAccount, kernelClient } = await login(config)
  console.log("Logged in as:", sessionKeyAccount.address)
 
  // User is authenticated and ready to interact
} catch (error) {
  if (error.message.includes("User rejected")) {
    console.log("User cancelled authentication")
  } else if (error.message.includes("not found")) {
    console.log("Account not found - user needs to register first")
  } else {
    console.error("Login failed:", error)
  }
}

What Happens During Login

  1. Passkey Authentication: Browser prompts user to authenticate with their passkey
  2. Account Recovery: The smart contract wallet is recovered using the passkey
  3. Session Restoration: A new session key is generated for interactions
  4. Storage Update: Fresh authentication data is stored locally

reconnect()

Restores a previous session using stored authentication data, avoiding the need for passkey authentication.

Function Signature

function reconnect(config: Config): Promise<{ sessionKeyAccount: SessionKeyAccount; kernelClient: KernelClient } | null>

Parameters

  • config: Configuration object from createConfig()

Usage Example

// Try to restore previous session
const result = await reconnect(config)
 
if (result) {
  const { sessionKeyAccount, kernelClient } = result
  console.log("Session restored for:", sessionKeyAccount.address)
  // User is automatically authenticated
} else {
  console.log("No previous session found")
  // User needs to login or register
}

When to Use Reconnect

  • App Startup: Check if user has an active session
  • Page Refresh: Restore authentication state
  • Background Tasks: Maintain authentication for scheduled operations

disconnect()

Clears stored authentication data and ends the current session.

Function Signature

function disconnect(): Promise<void>

Usage Example

await disconnect()
console.log("User logged out")
 
// All stored authentication data is cleared
// User will need to login again to interact

What Gets Cleared

  • Session keys and authentication tokens
  • Locally stored account data
  • Any cached user information

hasAccount()

Checks if the user has an existing account stored locally.

Function Signature

function hasAccount(): Promise<boolean>

Usage Example

const userHasAccount = await hasAccount()
 
if (userHasAccount) {
  // Try to reconnect or show login UI
  const result = await reconnect(config)
  if (!result) {
    // Show login form
  }
} else {
  // Show registration form
}

Authentication Flow Examples

Complete Authentication Flow

import { createConfig, hasAccount, reconnect, login, register } from "@humanwallet/core"
 
const config = createConfig({
  projectId: "your-project-id",
  chain: sepolia,
})
 
async function authenticateUser(username?: string) {
  // Check if user has existing account
  const hasExistingAccount = await hasAccount()
 
  if (hasExistingAccount) {
    // Try to restore session
    const session = await reconnect(config)
    if (session) {
      return session // User is authenticated
    }
 
    // Session expired, need to login
    return await login(config)
  } else {
    // New user, need to register
    if (username) {
      return await register(username, config)
    } else {
      throw new Error("Username required for registration")
    }
  }
}

React Hook Example

import { useState, useEffect } from "react"
import { hasAccount, reconnect, login, register, disconnect } from "@humanwallet/core"
 
export function useAuth(config) {
  const [account, setAccount] = useState(null)
  const [isLoading, setIsLoading] = useState(true)
 
  // Check for existing session on mount
  useEffect(() => {
    async function checkAuth() {
      try {
        const session = await reconnect(config)
        if (session) {
          setAccount(session.sessionKeyAccount)
        }
      } catch (error) {
        console.error("Reconnect failed:", error)
      } finally {
        setIsLoading(false)
      }
    }
 
    checkAuth()
  }, [config])
 
  const loginUser = async () => {
    setIsLoading(true)
    try {
      const { sessionKeyAccount } = await login(config)
      setAccount(sessionKeyAccount)
      return sessionKeyAccount
    } finally {
      setIsLoading(false)
    }
  }
 
  const registerUser = async (username: string) => {
    setIsLoading(true)
    try {
      const { sessionKeyAccount } = await register(username, config)
      setAccount(sessionKeyAccount)
      return sessionKeyAccount
    } finally {
      setIsLoading(false)
    }
  }
 
  const logoutUser = async () => {
    await disconnect()
    setAccount(null)
  }
 
  return {
    account,
    isLoading,
    isAuthenticated: !!account,
    login: loginUser,
    register: registerUser,
    logout: logoutUser,
  }
}

Error Handling

Common Error Types

try {
  await register("username", config)
} catch (error) {
  if (error.message.includes("User rejected")) {
    // User cancelled passkey creation/authentication
    console.log("Authentication cancelled by user")
  } else if (error.message.includes("not supported")) {
    // Browser doesn't support WebAuthn
    console.log("Passkeys not supported in this browser")
  } else if (error.message.includes("network")) {
    // Network connectivity issues
    console.log("Network error - please try again")
  } else if (error.message.includes("already exists")) {
    // Username already taken (for register)
    console.log("Username already exists")
  } else {
    // Other errors
    console.error("Authentication error:", error)
  }
}

Browser Compatibility

Passkey authentication requires modern browser support:

  • Chrome: 67+
  • Firefox: 60+
  • Safari: 14+
  • Edge: 18+

For unsupported browsers, consider showing a fallback message or alternative authentication method.

Security Considerations

  • Username Privacy: Usernames are used for passkey creation but aren't stored on-chain
  • Session Keys: Temporary keys are used for transactions to avoid repeated passkey prompts
  • Local Storage: Authentication data is stored securely in browser storage
  • Network Security: All communications use HTTPS and secure protocols