Developing Session Keys System for dApp
Imagine a GameFi protocol: user buys an item, makes a move, crafts a weapon — each action requires a MetaMask signature. Over an hour of play — 30-50 pop-up windows. User leaves after the fifth one. Session keys solve this: user signs once, granting limited rights to a temporary key, which signs the rest automatically.
How Session Keys Work
The basic concept is delegating signatures with restrictions. The main account (EOA or smart wallet) grants a session key rights to execute specific operations for a defined time. The session key is stored in browser memory or a backend service, signs transactions without user participation, and automatically loses authority at session end.
This only works with smart wallets (Account Abstraction) — EOAs can't delegate signatures with restrictions. So session keys are inseparably tied to EIP-4337.
Architecture in EIP-4337 Context
In standard EIP-4337 flow: smart wallet (Account) receives UserOperation, its validateUserOp checks the signature. For session keys the logic expands:
-
validateUserOpchecks if the signature is from a session key - If yes — checks if the key is registered in the contract's session storage
- Checks if the validity period hasn't expired (
validUntiltimestamp) - Checks if the called function is allowed (whitelist by
selector) - Checks if operation limit or session-wide limit isn't exceeded
This pattern is implemented in Kernel (ZeroDev), Biconomy Smart Account v2, and Rhinestone Module SDK. Each implementation differs slightly in permission object structure, but the logic is the same.
Common Implementation Pitfalls
Scope Too Wide
Most common mistake when granting session key — overly broad rights. "Can call any function of contract X" instead of "can call only buyItem(uint256) with amount ≤ 10 USDC". Wide scope defeats the purpose of session keys: a compromised session key gives the attacker full account access in that contract context.
Correct permission structure includes:
-
target— contract address allowed to interact with -
selector— specific function selector (4 bytes) -
valueLimit— maximum ETH per transaction -
callCountLimit— maximum calls per session -
validAfter/validUntil— time boundaries
Storing Key on Client
A session key is a private key. Storing it in localStorage directly — dangerous, XSS attack leaks the key. Right approach: sessionStorage (clears on tab close) or encryption via WebCrypto API with user-specific key.
For more serious systems — session key is generated in WebWorker with isolated context, or moved to backend with signature provision via API (backend never sees the full main account key).
No Revocation Mechanism
Session should be revoked in three scenarios: user clicks "logout", session expires, suspicious activity detected. Revocation is implemented through revokeSession(bytes32 sessionKeyHash) in wallet contract — function marks the key as invalid in an on-chain mapping. After this, all transactions with the key will revert in validateUserOp.
Without revocation, session key leak means attack continues until time expires.
Implementation Stack
ZeroDev Kernel — most mature implementation with plugin architecture. Session key installs as plugin through kernel.execute(installPlugin(sessionKeyPlugin)). Supports ERC-7579 (modular smart accounts), allowing plugin reuse between different wallet implementations.
Rhinestone Module SDK — modular approach where each permission type is a separate validator. Flexible but requires ERC-7579 interface understanding.
permissionless.js from Pimlico — client library for EIP-4337, including session keys. Integrates with viem and wagmi.
Bundler for UserOperation submission: Alto (Pimlico) or Stackup — both support Polygon, Ethereum, Arbitrum, Optimism.
Development Process
Permission schema design (1-2 days). Determine which operations are allowed in a session, what limits apply, how the key is stored on the client.
Smart contract: session key validator (3-5 days). Custom validator for Kernel or using ready-made with permission configuration. Tests in Foundry with UserOperation flow emulation.
Client side (3-5 days). Session key generation, granting through main account signature, storage in sessionStorage, UserOperation signing with session key, submission via bundler API.
Lifecycle management (1-2 days). UI for viewing active sessions, revoking specific session, auto-revoke on logout.
Timeline Estimates
Basic system with one permission type on existing smart wallet — 1-2 weeks. Custom implementation with complex permission matrix and non-standard restrictions — 3-5 weeks. Integration with existing dApp not using Account Abstraction — requires prior AA migration, doubling the timeline.
Cost is calculated after auditing current wallet interaction architecture.







