5.6 KiB
Xpensely Server — API Reference
Last updated: 2026-05-09 · Branch:
feature/security-hardening
Table of Contents
- Overview
- Authentication
- Rate Limiting
- Endpoints
- 4.1 Home
- 4.2 Users
- 4.3 Expense Lists
- Data Models
- Error Handling
- Recent Changes —
feature/security-hardening
1. Overview
Xpensely Server is a Spring Boot REST API that manages shared expense lists for pairs of users. It uses Google OAuth2 JWT tokens for authentication. All protected endpoints require a valid Bearer token in the Authorization header.
Base URL (local dev): http://localhost:8080
Content-Type: application/json for all request and response bodies.
Public endpoints (no auth required):
| Method | Path | Description |
|---|---|---|
| GET | / |
Health check — returns "Welcome" |
| POST | /api/users/createUser |
Register a new user |
| GET | /api/users/byName |
Look up a user by username |
All other endpoints require authentication (see Section 2).
2. Authentication
The server uses OAuth2 Resource Server authentication with Google ID JWT tokens.
How it works
- The client authenticates with Google and receives a Google ID JWT.
- Every protected API request must include this token in the header:
Authorization: Bearer <google-id-jwt>
- The server validates the JWT signature and extracts the
subclaim (Google User ID). - The
subvalue is used to look up the registeredAppUserin the database viaAuthenticatedUserResolver. - If no
AppUserexists for that Google ID, the request is rejected with 403 Forbidden.
Test profile
When the application runs under the test Spring profile (-Dspring.profiles.active=test), all security is disabled — every endpoint is accessible without a token. This is used for automated tests only.
User registration flow
Before a user can call any protected endpoint they must first be registered:
- Authenticate with Google to obtain a Google ID JWT.
- Call
POST /api/users/createUserwith the JWT'ssubvalue asgoogleIdand a chosenusername. - All subsequent protected calls use the same JWT — the server resolves the caller automatically.
3. Rate Limiting
All requests pass through a RateLimitFilter (implemented with Bucket4j).
| Setting | Value |
|---|---|
| Limit | 60 requests per minute |
| Window | Rolling 1-minute bucket |
| Key (authenticated) | JWT sub claim (Google User ID) |
| Key (unauthenticated) | X-Forwarded-For header, falling back to remote IP |
When the limit is exceeded the server responds with:
HTTP 429 Too Many Requests
No Retry-After header is currently returned. Clients should back off and retry after 60 seconds.
Note: Rate limiting applies in the
!testprofile only. Tests run without rate limiting.
4. Endpoints
4.1 Home
GET /
Health check. No authentication required.
Response: 200 OK
Welcome
4.2 Users
Base path: /api/users
POST /api/users/createUser — Register a user
Auth required: No
Request body:
{
"username": "alice",
"googleId": "118400012345678901234"
}
| Field | Type | Constraints |
|---|---|---|
username |
String | Required. 3–30 chars. Pattern: ^[a-zA-Z0-9_.\-]+$ |
googleId |
String | Required. Non-blank. Must match the JWT sub from Google. |
Success response: 200 OK — returns the created AppUser object.
Error responses:
| Status | Condition |
|---|---|
| 400 | Validation failure (field errors returned as {"fieldName": "message"}) |
| 409 | username already taken |
GET /api/users — Get user by ID
Auth required: Yes
Query params:
| Param | Type | Required | Description |
|---|---|---|---|
id |
Long | Yes | Database ID of the user |
Success response: 200 OK — returns AppUser.
Error responses:
| Status | Condition |
|---|---|
| 404 | No user found for id |
GET /api/users/byName — Get user by username
Auth required: No
Query params:
| Param | Type | Required | Description |
|---|---|---|---|
username |
String | Yes | Exact username (case-sensitive) |
Success response: 200 OK — returns AppUser.
Error responses:
| Status | Condition |
|---|---|
| 404 | No user found for username |
GET /api/users/byGoogleId — Get user by Google ID
Auth required: Yes
Query params:
| Param | Type | Required | Description |
|---|---|---|---|
id |
String | Yes | Google sub claim |
Success response: 200 OK — returns AppUser.
Error responses:
| Status | Condition |
|---|---|
| 404 | No user found for that Google ID |
DELETE /api/users — Delete a user
Auth required: Yes
Query params:
| Param | Type | Required | Description |
|---|---|---|---|
id |
Long | Yes | Database ID of the user to delete |
Success response: 200 OK — returns the deleted AppUser.
Error responses:
| Status | Condition |
|---|---|
| 404 | No user found for id |
4.3 Expense Lists
TODO
5. Data Models
TODO
6. Error Handling
TODO
7. Recent Changes — feature/security-hardening
TODO