Feature: Authentication #5
Labels
No labels
area:api
area:core
area:docs
area:infra
area:ux
dependencies
documentation
duplicate
good first issue
help wanted
invalid
question
rust
status:complete
status:partial
status:planned
type:bug
type:design
type:feature
type:infra
type:refactor
type:research
type:ux
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
icub3d/decentcom#5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Migrated from GitHub issue icub3d/decentcom#5
Original Author: @icub3d
Original Date: 2026-04-15T14:15:24Z
Feature: Authentication
Overview
Implement the Ed25519 challenge-response authentication protocol between the client and server. The client sends its public key, the server returns a nonce challenge, the Tauri core signs the nonce, and the server verifies the signature and issues a session token. On first authentication, the server automatically registers the user. This replaces traditional username/password auth with cryptographic identity verification.
Background
The authentication flow is defined in the architecture doc (architecture.md) and the identity doc (identity.md). The server never stores passwords or secrets -- only public keys. Session tokens are short-lived. The storage layer (feature #3) provides
UserStoreandSessionStorefor persistence. The identity feature (#4) provides the client-side key pair and signing capability.Requirements
POST /api/v1/auth/challengeaccepts a public key and returns a nonce challengePOST /api/v1/auth/verifyaccepts a public key and signature, verifies the signature against the stored challenge, and returns a session tokensessionstableAuthorization: Bearer <token>header401 UnauthorizedDesign
API / Interface Changes
New REST endpoints:
POST /api/v1/auth/challengeRequest:
Response (200):
Errors:
400 Bad Request-- invalid public key formatPOST /api/v1/auth/verifyRequest:
Response (200):
Errors:
400 Bad Request-- missing fields or invalid format401 Unauthorized-- signature verification failed or challenge expired/not foundData Model Changes
New table (added to existing migration or a new migration):
Alternatively, challenges can be stored in-memory (a
DashMaportokio::sync::RwLock<HashMap>) since they are short-lived. An in-memory approach is simpler and avoids database writes for every auth attempt.Component Changes
New files:
Modified files:
Auth middleware extractor:
Client auth flow (in
client/src/api/auth.ts):POST /api/v1/auth/challengewith the public keysignIPC command with the noncePOST /api/v1/auth/verifywith the public key and signatureAuthorization: Bearer <token>to all subsequent requestsTask List
Server-side
ed25519-dalek,bs58,base64,rand,dashmaptoserver/Cargo.tomlshared/src/auth.rs(ChallengeRequest,ChallengeResponse,VerifyRequest,VerifyResponse)server/src/auth/challenge.rswith TTL-based expiry and background cleanup taskPOST /api/v1/auth/challengehandler: validate pubkey format, generate 32-byte random nonce, store challenge, return noncePOST /api/v1/auth/verifyhandler: look up challenge by pubkey, verify Ed25519 signature, consume challenge (single-use), create or fetch user, create session, return tokenAuthUserextractor inserver/src/auth/middleware.rs/api/v1/auth/in the routerGET /api/v1/auth/me) that returns the authenticated user's infoClient-side
client/src/api/auth.tswith functions for the challenge-response flowuseAuthhook that manages authentication state (unauthenticated, authenticating, authenticated)Test List
AuthUserextractor returns user for valid tokenAuthUserextractor returns 401 for missing/invalid/expired tokenGET /api/v1/auth/mereturns current user when authenticateduseAuthhook transitions through authentication states correctlyImplementation Notes
DashMap(not the database). TheChallengeStoreisArc-backed and held directly inAppState.SessionStore; no separatesession.rsfile was needed.AuthUserextractor does ISO string comparison for expiry checking (lexicographic string ordering works correctly for ISO 8601 timestamps).GET /api/v1/auth/meendpoint omits pubkey lookup for now — it only returnsuser_id. Pubkey will be added when the profiles feature is implemented.server.src.config.auth.session_ttl_secondswas added so session expiry is configurable with a 24-hour default./auth/challenge.Open Questions
SessionStoretrait makes it easy to swap later./auth/challengeto prevent nonce-flooding? This is a good idea but may be better addressed by a general rate-limiting middleware in a later feature.allowlistmembership mode, the server should reject unknown public keys rather than auto-registering them. The membership mode check should gate auto-registration.