gRPC Authentication
Login
Authenticate with email and password to get a JWT token. Login is rate-limited — after too many failed attempts for an email, further attempts are temporarily blocked (configurable via max_login_attempts and login_lockout_seconds in crap.toml).
grpcurl -plaintext -d '{
"collection": "users",
"email": "admin@example.com",
"password": "secret123"
}' localhost:50051 crap.ContentAPI/Login
The response contains a token and the user document.
Bearer Token
Pass the token via the authorization metadata header:
grpcurl -plaintext \
-H "authorization: Bearer eyJhbGciOi..." \
-d '{"collection": "posts"}' \
localhost:50051 crap.ContentAPI/Find
The token is extracted from the authorization metadata and validated. The authenticated user is available to access control functions.
Get Current User
Use the Me RPC to validate a token and get the user:
grpcurl -plaintext -d '{
"token": "eyJhbGciOi..."
}' localhost:50051 crap.ContentAPI/Me
Token Expiry
Tokens expire after token_expiry seconds (default: 7200 = 2 hours). Configurable globally in crap.toml or per auth collection.
Security
- Rate limiting — per-email tracking. After
max_login_attempts(default: 5) failures, the email is locked out forlogin_lockout_seconds(default: 300). Per-IP rate limiting (max_ip_login_attemptsin[auth]) provides additional protection against credential stuffing across multiple accounts. - Timing safety — login always performs a full Argon2id hash comparison, even for non-existent users, preventing timing-based email enumeration.
- JWT persistence — when no
secretis set incrap.toml, an auto-generated secret is persisted todata/.jwt_secretso tokens survive server restarts. - Account locking — when a user’s
_lockedfield is truthy, all authenticated requests (includingMe) are rejected withunauthenticatedstatus. This takes effect immediately, even for valid unexpired tokens.
Creating Users via gRPC
Include password in the data field of a Create request:
grpcurl -plaintext -d '{
"collection": "users",
"data": {
"email": "new@example.com",
"password": "secret123",
"name": "New User",
"role": "editor"
}
}' localhost:50051 crap.ContentAPI/Create
The password field is extracted, hashed with Argon2id, and stored separately. It never appears in the response.
Updating Passwords
Include password in the data field of an Update request:
grpcurl -plaintext -d '{
"collection": "users",
"id": "abc123",
"data": { "password": "new-password" }
}' localhost:50051 crap.ContentAPI/Update
If password is omitted, the existing password is kept.