Tutorial

Authentication

Every request to the API carries a session token, an opaque 256 bit bearer value that the service stores hashed and compares, and never decodes. This page covers obtaining one, using it, and how your identity becomes your wallet.

There are two distinct credentials in the platform.

CredentialAnswersLifetime
Session tokenWhich account is callingWhole account, until sign out or expiry
UCAN delegationWhat this DID may do to this one resourceDays, one grant, signed by a wallet

A session token authenticates the account. A UCAN delegation grants a narrow, delegable capability over a specific resource, and only a wallet can sign one. You always need the first, and you use the second to share.

1. Register

bash
curl -X POST https://api.ekayana.io/api/signup \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ada",
    "email": "ada@lab.example",
    "password": "correct-horse-battery"
  }'

username must be 3 to 50 letters, digits or underscores, email must parse as an address, and password must be 8 to 1024 characters. Signup answers 201.

json
{ "token": "3kq9...<43 characters of base64url>",
  "user": { "id": 42, "username": "ada", "email": "ada@lab.example", "did": null, "wallet": null } }

2. Sign in

bash
curl -X POST https://api.ekayana.io/api/signin \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@lab.example", "password": "correct-horse-battery" }'

Returns the same { token, user } shape.

3. Call an authenticated endpoint

bash
curl https://api.ekayana.io/api/users/me \
  -H "Authorization: Bearer $TOKEN"
json
{ "id": 42, "username": "ada", "email": "ada@lab.example", "did": null, "wallet": null }

The header must be exactly Authorization: Bearer <token>. A missing header, a malformed one, or a token that fails verification all return an auth error before the handler runs.

did and wallet are null until a wallet is linked. Any call that needs an identity, such as creating a DID or sharing a file, answers 409 until then.

To find collaborators to share with, GET /api/users/search?q=<query> matches username or email, with a minimum of 2 characters and at most 10 results, and returns each user's did. That is what the sharing flow consumes.

4. What a session token is

The token is 32 random bytes, base64url encoded. The service stores its SHA-256 hash and looks the hash up on every request. The token itself is never written down and carries no claims to tamper with. Two properties follow.

  • There is nothing to forge. There is no signature to break and no payload to edit. Guessing a token means guessing 256 bits.
  • There is nothing to leak on the server. A database dump exposes hashes, which do not authenticate anyone.

POST /api/signout deletes the session. Sessions also expire after SESSION_TTL_SECS, 24 hours by default. There is no refresh window. When a session ends, sign in again.

Your identity on the platform is your Solana wallet. Its address on the configured cluster is your did:bio, and every dataset you create is a DID that wallet owns and signs for. The service holds no signing key for you.

Linking is a Sign In With Solana exchange. POST /api/wallet/nonce returns a message naming your account and a one time nonce. The wallet signs it. POST /api/wallet/verify takes the address and the signature and records the link. From then on /api/users/me reports your did, and the wallet can sign delegations and registry transactions. The app wraps this in one button.

json
{ "id": 42, "username": "ada", "email": "ada@lab.example",
  "did": "did:bio:devnet:HP3Hj9en...", "wallet": "HP3Hj9en..." }

A wallet can also sign in directly. POST /api/wallet/challenge and POST /api/wallet/signin open a session for a linked wallet without a password, and POST /api/wallet/signup creates an account from one.

See did:bio identifiers for what the wallet's DID can do, and Sharing & Delegation for the first thing it signs.