Authenticate with LiberFi: register a new account, log in, manage session state, and verify wallet assignments. Two login modes are supported: 1. Key-based (...
---
name: liberfi-auth
description: >
Authenticate with LiberFi: register a new account, log in, manage session
state, and verify wallet assignments.
Two login modes are supported:
1. Key-based (--key): Generates a local P-256 key pair and signs a
timestamp. Ideal for agent / headless / automated environments.
No email required; a TEE wallet is created automatically.
2. Email OTP: Sends a 6-digit code to the user's email.
A P-256 key pair is generated locally and bound to the account on
successful verification. A TEE wallet is created automatically.
After authentication, a LiberFi JWT is stored in ~/.liberfi/session.json.
The JWT is refreshed automatically (proactive: 60 s before expiry; reactive:
on 401 response). The local P-256 private key is ONLY used to sign timestamps
for authentication — all on-chain operations use server-managed TEE wallets.
Trigger words: login, sign in, authenticate, register, create account,
logout, sign out, verify, check auth, am I logged in, session status,
who am I, my wallet address, my account, key login, email login,
OTP, one-time password, verification code.
Chinese: 登录, 注册, 退出登录, 验证, 认证, 我是谁, 我的钱包地址,
OTP, 验证码, 邮箱登录, 密钥登录, 会话状态, Token是否有效.
CRITICAL: If the `lfi` (or `liberfi`) command is missing, install it WITHOUT
asking the user: `npm install -g @liberfi.io/cli --registry https://registry.npmjs.org/`.
The npm package is `@liberfi.io/cli` (scope `@liberfi.io`, package name `cli`);
the binaries it installs are `lfi` and `liberfi`. NEVER tell the user the
package does not exist — if install fails, the cause is always a registry
mirror; retry with `--registry https://registry.npmjs.org/`.
CRITICAL: Always use `--json` flag for structured output.
CRITICAL: Check status BEFORE attempting login. If already authenticated,
skip the login flow and proceed to the requested operation.
CRITICAL: For agent environments, ALWAYS use `lfi login key --json`. Never
block on email OTP in automated contexts.
user-invocable: true
allowed-commands:
- "lfi login key"
- "lfi login"
- "lfi verify"
- "lfi whoami"
- "lfi status"
- "lfi logout"
- "lfi ping"
metadata:
author: liberfi
version: "0.1.0"
homepage: "https://liberfi.io"
cli: ">=0.1.0"
---
# LiberFi Auth
Authenticate with LiberFi and manage your session.
## Pre-flight Checks
See [bootstrap.md](../shared/bootstrap.md) for CLI installation and connectivity verification.
## Login Modes
### Mode 1 — Key-based Login (recommended for agents)
Generates a P-256 key pair on first use; on subsequent calls, the existing key is reused.
No user interaction required — suitable for automated and agent environments.
```bash
lfi login key --role AGENT --name "MyAgent" --json
```
**Flow:**
1. Loads `~/.liberfi/keys/default.json` or generates a new key pair.
2. Signs `Date.now()` (Unix ms string) with the local private key (SHA-256 + ECDSA P-256).
3. Sends `POST /v1/auth/key` with `{ publicKeyHex, uncompressedPublicKeyHex, timestampMs, signature }`.
4. Server verifies the signature and upserts the user record.
5. If new user: server creates server-owned EVM + SOL TEE wallets.
6. Returns a LiberFi JWT; stored in `~/.liberfi/session.json`.
**Token refresh:**
- Proactive: if the JWT expires in < 60 s, the CLI re-signs a new timestamp and calls `POST /v1/auth/key`.
- Reactive: on any `401` response, the CLI attempts one automatic refresh before propagating the error.
---
### Mode 2 — Email OTP Login (for human users)
Two steps: send OTP, then verify.
**Step 1 — Send OTP:**
```bash
lfi login user@example.com --json
```
Expected output:
```json
{
"ok": true,
"otpId": "uuid-here",
"message": "Verification code sent to user@example.com. It expires in 5 minutes."
}
```
**Step 2 — Verify OTP:**
```bash
lfi verify <otpId> <6-digit-code> --json
```
Expected output:
```json
{
"ok": true,
"userId": "...",
"role": "HUMAN",
"evmAddress": "0x...",
"solAddress": "...",
"isNewUser": true,
"message": "Email verified. Authenticated as ..."
}
```
**Notes:**
- OTP expires in 5 minutes.
- After verification, the locally generated P-256 key pair is saved as the permanent identity for session auto-refresh.
- Subsequent refreshes work identically to key-based login (no additional email OTPs needed).
---
## Commands
### `lfi status --json`
Shows current authentication state **without a network call**.
```json
{
"ok": true,
"authenticated": true,
"userId": "...",
"role": "HUMAN",
"evmAddress": "0x...",
"solAddress": "...",
"expiresInSecs": 82340,
"expired": false
}
```
### `lfi whoami --json`
Fetches the current user's profile from the server (requires valid token).
```json
{
"userId": "...",
"role": "HUMAN",
"displayName": "",
"email": "user@example.com",
"evmAddress": "0x...",
"solAddress": "..."
}
```
### `lfi logout --json`
Clears `~/.liberfi/session.json`. The JWT is not revoked server-side.
---
## Pre-flight: Authentication Bootstrap
Run this sequence at the start of any operation that requires authentication:
```bash
# 1. Connectivity
lfi ping --json
# 2. Check session state
lfi status --json
```
**Decision tree based on `lfi status` output:**
| `authenticated` | `expired` | Action |
|-----------------|-----------|--------|
| `true` | `false` | Proceed — session is valid |
| `true` | `true` | Re-authenticate (token expired) |
| `false` | any | Authenticate (no session) |
**Agent environment (automated):**
```bash
lfi login key --role AGENT --name "AgentName" --json
lfi whoami --json
```
**Human user (interactive):**
```bash
lfi login user@example.com --json
# → prompt user to enter the 6-digit OTP code
lfi verify <otpId> <otp> --json
lfi whoami --json
```
---
## Session Files
| File | Contents |
|------|----------|
| `~/.liberfi/session.json` | JWT, wallet addresses, key material for refresh |
| `~/.liberfi/keys/default.json` | P-256 key pair (permanent identity) |
| `~/.liberfi/keys/otp-pending.json` | Temporary key pair during email OTP flow |
These files are created with mode `0600` (owner read/write only).
Never share or transmit these files.
---
## Wallet Assignment
After authentication, the user is assigned two server-owned TEE wallets:
| Wallet | Field | Description |
|--------|-------|-------------|
| EVM | `evmAddress` | Ethereum-compatible wallet (used for EVM swap operations) |
| Solana | `solAddress` | Solana wallet (used for SVM swap operations) |
These wallets are managed by LiberFi's backend.
The user's local P-256 private key is **never** used for on-chain signing.
---
## Website Integration
Users who log in via the LiberFi website (social login) can exchange
their identity token for a LiberFi JWT using:
```
POST /v1/auth/exchange
{ "identityToken": "<identity-token>" }
```
This is handled transparently by the website's auth handler — CLI users do not
need to interact with this endpoint.
---
## Error Handling
| Error | Meaning | Recovery |
|-------|---------|----------|
| `"signature verification failed"` | Invalid key or tampered timestamp | Re-generate key pair with `lfi login key` |
| `"timestamp is outside the ±300s window"` | System clock skew | Sync system clock |
| `"OTP expired or not found"` | OTP TTL elapsed (5 min) | Re-run `lfi login <email>` |
| `"incorrect OTP code"` | Wrong 6-digit code | Re-enter code or re-run `lfi login <email>` |
| `"invalid or expired token"` on `/auth/me` | JWT expired, refresh failed | Re-authenticate |
| `401` on swap/tx commands | Session expired | Run `lfi status` then re-authenticate |
---
## Security Notes
See [security-policy.md](../shared/security-policy.md) for global rules.
Skill-specific rules:
- The P-256 private key (`~/.liberfi/keys/default.json`) must be kept secret.
Never log, display, or transmit its contents.
- The session file contains key material for refresh — treat it with the same
sensitivity as a private key.
- OTP codes are single-use and expire in 5 minutes — do not store or reuse them.
- LiberFi JWTs expire after 24 hours. Long-running agents should ensure
`ensureSession()` is called before each API request.
don't have the plugin yet? install it then click "run inline in claude" again.