Authentication
There are several ways to authenticate with Nava. Choose based on how you want to handle credentials and how tightly you want to integrate.
Authentication Modes
| Mode | How It Works | Best For |
|---|---|---|
| Non-interactive (API Key) | Obtain a Nava API key ahead of time via the Nava UI and configure it locally. The agent never needs to prompt the user for credentials. | SDK, MCP (local), skill (non-interactive) |
| Headless Wallet (SIWE) | The agent signs a SIWE message with its wallet, exchanges it for JWT tokens, then creates a session API key via POST /user-api-keys. No Nava UI interaction required. | Autonomous agents with signing capability |
| Interactive (OAuth) | The MCP client handles OAuth with the Nava authorization server. The user logs in through the Nava UI in their browser. | MCP (remote), zero-setup for end users |
| Interactive (Code-based) | The agent requests a short-lived login code, then prompts the user to visit the Nava UI and enter the code. Once authenticated, the agent receives an API key for the session. | Any agent with shell access, no pre-provisioned key |
Non-Interactive Setup
- Sign up at Nava UI and create an API key.
- Store the key as
NAVA_API_KEYand your wallet address asWALLET_ADDRESSin your environment.
Headless Wallet (SIWE → API Key Bootstrap)
Use this when the runtime can sign messages (local wallet lib, hardware signer, or wallet MCP/tool adapter).
import { createClientFromWallet } from '@navalabs/sdk';
const signer = {
async getAddress() {
return wallet.address;
},
async signMessage(message: string) {
return wallet.signMessage(message);
},
};
const { client, apiKey, walletAddress } = await createClientFromWallet({
signer,
baseUrl: 'https://internal.navalabs.dev/api',
domain: 'testnet.navalabs.dev',
uri: 'https://internal.navalabs.dev/api',
chainId: 11155111,
apiKeyName: 'agent-session',
});
Bootstrap sequence:
POST /auth/siwe/nonce- Build SIWE message with
domain,address,statement,uri,version: "1",chainId,nonce,issuedAt - Sign the exact SIWE message string
POST /auth/siwe/verifywith{ message, signature }POST /user-api-keyswithAuthorization: Bearer <accessToken>- Use returned API key for all subsequent SDK/MCP/API calls
Lower-level helpers are exported for external wallet toolchains: prepareSiweAuthentication, verifySiweAuthentication, createUserApiKey, and buildSiweMessage.
Interactive (OAuth): MCP Remote
The MCP client handles this automatically. See MCP Server (Remote) for setup.
MCP Client Nava MCP Server Nava UI (Browser)
│ │ │
├── connect ─────────────────►│ │
│◄── OAuth challenge ────────┤ │
│── open browser ────────────┼───────────────────────────►│
│ │◄── user logs in & grants ──┤
│◄── authenticated ──────────┤ │
├── requestVerification ────►│ │
│◄── result ─────────────────┤ │
Interactive (Code-Based)
For agents that can run shell commands but don’t have a pre-provisioned key.
Step 1: Initiate authentication
curl -s -X POST "https://internal.navalabs.dev/api/auth/connect/initiate" \
-H "Content-Type: application/json"
Response contains:
code: a 32-character hex connection code (expires after 10 minutes)authUrl: a URL for the user to visit
Present the authUrl to the user and ask them to open it in their browser, sign in, and approve the connection.
Step 2: Poll for API key
curl -s -X GET "https://internal.navalabs.dev/api/auth/connect/status?code=<code>" \
-H "Content-Type: application/json"
Poll every 3-5 seconds. When the user completes authentication:
status:"ready"apiKey: the provisioned API key (returned exactly once; subsequent polls return410 Gone)walletAddress: the authenticated wallet address
Store these for the remainder of the session.
API Key Types
The service supports multiple API key types:
- USER keys: User-specific operations (
:ownscopes). Created viaPOST /user-api-keys(requires JWT auth). - ADMIN keys: Administrative/agent operations (
:anyscopes). - ORION keys: Arbiter/orion approval operations.
Scopes
transactions:create:own: Create transactions for own accounttransactions:create:any: Create transactions for any accounttransactions:approve:own: Approve own transactionstransactions:read:own: Read own transactions