Overview
Session keys are disposable keys that allow dapps to perform actions on a user’s behalf without exposing the main wallet. They provide:
Delegated signing for storage operations
Scoped permissions with expiration
Reduced wallet prompts for better UX
Security through limited authorization
Architecture
Quick Start
import * as SessionKey from '@filoz/synapse-core/session-key'
import { Synapse } from '@filoz/synapse-sdk'
import { generatePrivateKey } from 'viem/accounts'
import { calibration } from '@filoz/synapse-core/chains'
// Generate new session key
const sessionPrivateKey = generatePrivateKey ()
// Create session key account
const sessionKey = SessionKey . fromSecp256k1 ({
privateKey: sessionPrivateKey ,
root: mainAccount . address ,
chain: calibration ,
})
console . log ( 'Session key address:' , sessionKey . address )
console . log ( 'Root address:' , sessionKey . rootAddress )
import { login } from '@filoz/synapse-core/session-key'
import * as Permissions from '@filoz/synapse-core/session-key/permissions'
// Define permissions and expiration (30 days)
const expiry = BigInt ( Math . floor ( Date . now () / 1000 ) + 30 * 24 * 3600 )
const hash = await login ( mainClient , {
sessionKeyAddress: sessionKey . address ,
permissions: Permissions . FWSSAllPermissions ,
expiry ,
})
await mainClient . waitForTransactionReceipt ({ hash })
console . log ( 'Session key authorized' )
Use Session Key with Synapse
const synapse = new Synapse ({
client: mainClient ,
sessionClient: sessionKey . client ,
})
// All operations now use session key for signing
const result = await synapse . storage . upload ( data )
console . log ( 'Uploaded using session key' )
Permissions
FWSS Permissions
Filecoin Warm Storage Service has several permission types:
import * as Permissions from '@filoz/synapse-core/session-key/permissions'
// Individual permissions
const createDataSetPerm = Permissions . FWSSCreateDataSetPermission
const addPiecesPerm = Permissions . FWSSAddPiecesPermission
const terminatePerm = Permissions . FWSSTerminateDataSetPermission
const removePiecePerm = Permissions . FWSSRemovePiecePermission
// All permissions (recommended)
const allPerms = Permissions . FWSSAllPermissions
Grant Specific Permissions
import { login } from '@filoz/synapse-core/session-key'
// Only allow creating datasets and adding pieces
const hash = await login ( mainClient , {
sessionKeyAddress: sessionKey . address ,
permissions: [
Permissions . FWSSCreateDataSetPermission ,
Permissions . FWSSAddPiecesPermission ,
],
expiry: BigInt ( Math . floor ( Date . now () / 1000 ) + 86400 ), // 1 day
})
Check Permissions
// Check single permission
if ( sessionKey . hasPermission ( Permissions . FWSSCreateDataSetPermission )) {
console . log ( 'Can create datasets' )
}
// Check multiple permissions
if ( sessionKey . hasPermissions ( Permissions . FWSSAllPermissions )) {
console . log ( 'Has all FWSS permissions' )
}
// Get all expirations
const expirations = sessionKey . expirations
for ( const [ permission , expiry ] of Object . entries ( expirations )) {
console . log ( ` ${ permission } : expires at ${ new Date ( Number ( expiry ) * 1000 ) } ` )
}
Sync Permissions
Sync permissions from the blockchain:
// Sync all permissions
await sessionKey . syncExpirations ()
// Sync specific permissions
await sessionKey . syncExpirations ([
Permissions . FWSSCreateDataSetPermission ,
Permissions . FWSSAddPiecesPermission ,
])
Watch for Changes
Listen for permission updates:
// Start watching
const unwatch = await sessionKey . watch ()
// Listen for updates
sessionKey . addEventListener ( 'expirationsUpdated' , ( event ) => {
console . log ( 'Permissions updated:' , event . detail )
})
sessionKey . addEventListener ( 'connected' , ( event ) => {
console . log ( 'Watching started:' , event . detail )
})
sessionKey . addEventListener ( 'disconnected' , () => {
console . log ( 'Watching stopped' )
})
sessionKey . addEventListener ( 'error' , ( event ) => {
console . error ( 'Watch error:' , event . detail )
})
// Stop watching
unwatch ()
// or
sessionKey . unwatch ()
Revoke Session Key
import { revoke } from '@filoz/synapse-core/session-key'
const hash = await revoke ( mainClient , {
sessionKeyAddress: sessionKey . address ,
})
await mainClient . waitForTransactionReceipt ({ hash })
console . log ( 'Session key revoked' )
Query Authorizations
import { getExpirations } from '@filoz/synapse-core/session-key'
const expirations = await getExpirations ( client , {
address: mainAccount . address ,
sessionKeyAddress: sessionKey . address ,
permissions: Permissions . FWSSAllPermissions ,
})
for ( const [ permission , expiry ] of Object . entries ( expirations )) {
const isExpired = expiry < BigInt ( Math . floor ( Date . now () / 1000 ))
console . log ( ` ${ permission } : ${ isExpired ? 'expired' : 'valid' } ` )
}
Storage Operations
Session keys work seamlessly with storage:
const synapse = new Synapse ({
client: mainClient ,
sessionClient: sessionKey . client ,
})
// Upload (uses session key for signing)
const result = await synapse . storage . upload ( data , {
callbacks: {
onStored : ( providerId , pieceCid ) => {
console . log ( 'Stored with session key' )
},
},
})
// Download (session key not needed for reads)
const downloaded = await synapse . storage . download ({
pieceCid: result . pieceCid
})
Split Operations
Session keys with split operations:
const [ primary , secondary ] = await synapse . storage . createContexts ({
count: 2 ,
})
// Store (session key signs)
const stored = await primary . store ( data )
// Pre-sign for commit (session key signs)
const extraData = await secondary . presignForCommit ([
{ pieceCid: stored . pieceCid },
])
// Pull (session key auth in extraData)
await secondary . pull ({
pieces: [ stored . pieceCid ],
from : ( cid ) => primary . getPieceUrl ( cid ),
extraData ,
})
// Commit (session key signs via extraData)
await primary . commit ({ pieces: [{ pieceCid: stored . pieceCid }] })
await secondary . commit ({
pieces: [{ pieceCid: stored . pieceCid }],
extraData ,
})
Security Best Practices
Short Expiration Use short expiration times (hours or days, not months)
Minimal Permissions Grant only the permissions needed for the task
Secure Storage Store session keys securely (never commit to git)
Revoke When Done Revoke session keys when no longer needed
Expiration Management
// Check if expired
const now = BigInt ( Math . floor ( Date . now () / 1000 ))
const createExpiry = sessionKey . expirations [ Permissions . FWSSCreateDataSetPermission ]
if ( createExpiry < now ) {
console . log ( 'Permission expired, need to re-authorize' )
// Re-authorize
const hash = await login ( mainClient , {
sessionKeyAddress: sessionKey . address ,
permissions: Permissions . FWSSAllPermissions ,
expiry: now + 86400 n , // 1 day
})
await mainClient . waitForTransactionReceipt ({ hash })
await sessionKey . syncExpirations ()
}
Session Key Account
Create session key account without full session key:
import { accountFromSecp256k1 } from '@filoz/synapse-core/session-key'
import type { Hex } from 'viem'
const account = accountFromSecp256k1 ({
privateKey: '0x...' as Hex ,
rootAddress: mainAccount . address ,
})
console . log ( 'Account type:' , account . source ) // 'sessionKey'
console . log ( 'Key type:' , account . keyType ) // 'Secp256k1'
console . log ( 'Root:' , account . rootAddress )
Error Handling
try {
await synapse . storage . upload ( data )
} catch ( error ) {
if ( error . message . includes ( 'SessionKeyNotAuthorized' )) {
console . error ( 'Session key not authorized or expired' )
// Re-authorize session key
} else if ( error . message . includes ( 'InvalidSignature' )) {
console . error ( 'Session key signature invalid' )
}
}
Browser Usage
// Store session key in sessionStorage (cleared on tab close)
const sessionPrivateKey = generatePrivateKey ()
sessionStorage . setItem ( 'sessionKey' , sessionPrivateKey )
// Retrieve session key
const storedKey = sessionStorage . getItem ( 'sessionKey' ) as Hex
const sessionKey = SessionKey . fromSecp256k1 ({
privateKey: storedKey ,
root: mainAccount . address ,
chain: calibration ,
})
Never store session keys in localStorage - use sessionStorage or secure key management.
Complete Example
import { Synapse } from '@filoz/synapse-sdk'
import * as SessionKey from '@filoz/synapse-core/session-key'
import { login } from '@filoz/synapse-core/session-key'
import * as Permissions from '@filoz/synapse-core/session-key/permissions'
import { generatePrivateKey } from 'viem/accounts'
import { calibration } from '@filoz/synapse-core/chains'
// 1. Create main client
const mainClient = createWalletClient ({
chain: calibration ,
transport: http (),
account: privateKeyToAccount ( '0x...' ),
})
// 2. Generate session key
const sessionPrivateKey = generatePrivateKey ()
const sessionKey = SessionKey . fromSecp256k1 ({
privateKey: sessionPrivateKey ,
root: mainClient . account ,
chain: calibration ,
})
// 3. Authorize session key
const expiry = BigInt ( Math . floor ( Date . now () / 1000 ) + 86400 ) // 1 day
const hash = await login ( mainClient , {
sessionKeyAddress: sessionKey . address ,
permissions: Permissions . FWSSAllPermissions ,
expiry ,
})
await mainClient . waitForTransactionReceipt ({ hash })
// 4. Create Synapse with session key
const synapse = new Synapse ({
client: mainClient ,
sessionClient: sessionKey . client ,
})
// 5. Use storage operations
const result = await synapse . storage . upload ( data )
console . log ( 'Uploaded with session key:' , result . pieceCid )
// 6. Revoke when done
await revoke ( mainClient , {
sessionKeyAddress: sessionKey . address ,
})
Next Steps
Storage Operations Use session keys with storage operations
Session Key Registry Learn about the registry contract