Skip to main content

Authentication

Wallet public keys are the primary user identity.

CredentialPrefixLifetime
Session JWTNone1 hour
API keytoda_Until revoked

Both are sent as Authorization: Bearer <token>.

Sign-In With Solana​

1. Request a challenge​

POST /auth/challenge
{ "publicKey": "7xKX..." }

The server stores a 128-bit nonce and returns a structured SIWS message. The challenge expires after five minutes.

The signed message includes the domain, URI, chain ID, nonce, issue time, and expiry time.

2. Verify​

POST /auth/verify
{ "publicKey": "7xKX...", "message": "<message>", "signature": "<base58>" }

The server:

  1. parses the message and compares the address
  2. atomically consumes the stored nonce
  3. checks expiry
  4. compares the full message with the stored challenge
  5. verifies the ed25519 signature

A successful first sign-in also creates the user's wrapped data key.

Nonce storage​

Nonces are stored in Postgres so they remain single-use across restarts and multiple API instances. Concurrent verification attempts can consume a nonce only once.

Set SIWS_DOMAIN, SIWS_URI, and SIWS_CHAIN_ID to the production deployment values.

Session tokens​

The core API issues an HS256 JWT with sub, pk, iat, and exp claims.

  • JWT_SECRET must contain at least 32 characters.
  • Tokens expire after one hour.
  • The core API does not support refresh or early revocation.

The extension API uses random stateful sessions. Token hashes are stored in extension_sessions, allowing logout and server-side revocation.

Disclosure of JWT_SECRET allows sessions to be forged for any user.

API keys​

POST /keys
{ "label": "production" }

API keys contain 24 random bytes encoded as 48 hex characters after the toda_ prefix.

Only the SHA-256 hash and a display prefix are stored. The raw key is returned once.

GET /keys
DELETE /keys/:id

Revocation takes effect on the next request.

Bearer resolution​

prefix is "toda_" -> hash and query api_keys
otherwise -> verify JWT
then -> load user and wrapped data key

Authorization​

Memory queries are scoped to the authenticated userId. A request for another user's memory returns 404.

The core API has no roles or ACLs. Temporary sharing uses capability grants.

Verification access​

Authentication controls plaintext reads. Commitment verification is public and does not require a toda session.